Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge FRU stuff from vbm #553

Merged
merged 4 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions BossMod/Autorotation/Standard/StandardWAR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa
if (Player.InCombat && Unlocked(WAR.AID.Infuriate))
{
var stratInf = strategy.Option(Track.Infuriate);
var inf = ShouldUseInfuriate(stratInf.As<InfuriateStrategy>(), primaryTarget);
var inf = ShouldUseInfuriate(stratInf.As<InfuriateStrategy>(), primaryTarget, burstStrategy == BurstStrategy.IgnoreST);
if (inf.Use)
QueueOGCD(WAR.AID.Infuriate, Player, stratInf.Value.PriorityOverride, OGCDPriority.Infuriate, inf.Delayable ? ActionQueue.Priority.VeryLow : ActionQueue.Priority.Low);
}
Expand Down Expand Up @@ -837,7 +837,7 @@ private bool ShouldUseBerserk(OffensiveStrategy strategy, Actor? target, int aoe
}
}

private (bool Use, bool Delayable) ShouldUseInfuriate(InfuriateStrategy strategy, Actor? target)
private (bool Use, bool Delayable) ShouldUseInfuriate(InfuriateStrategy strategy, Actor? target, bool ignoreST)
{
if (strategy == InfuriateStrategy.Delay || CanFitGCD(NascentChaosLeft))
return (false, false); // explicitly forbidden or NC still active
Expand Down Expand Up @@ -915,7 +915,7 @@ private bool ShouldUseBerserk(OffensiveStrategy strategy, Actor? target, int aoe
_ => (30, 4)
};
// don't double infuriate during opener when NC is not yet unlocked (TODO: consider making it better)
if (Gauge + stRefreshGauge + 50 > 100 && !CanFitGCD(SurgingTempestLeft, stRefreshGCDs))
if (!ignoreST && Gauge + stRefreshGauge + 50 > 100 && !CanFitGCD(SurgingTempestLeft, stRefreshGCDs))
return (false, false);

// at this point, use under burst or delay outside (TODO: reconsider, we might want to be smarter here...)
Expand Down
6 changes: 4 additions & 2 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRU.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace BossMod.Dawntrail.Ultimate.FRU;

class P2QuadrupleSlap(BossModule module) : Components.TankSwap(module, ActionID.MakeSpell(AID.QuadrupleSlapFirst), ActionID.MakeSpell(AID.QuadrupleSlapFirst), ActionID.MakeSpell(AID.QuadrupleSlapSecond), 4.1f, null, true);
class P2CrystalOfLight(BossModule module) : Components.Adds(module, (uint)OID.CrystalOfLight);
class P3Junction(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.Junction));
class P3BlackHalo(BossModule module) : Components.CastSharedTankbuster(module, ActionID.MakeSpell(AID.BlackHalo), new AOEShapeCone(60, 45.Degrees())); // TODO: verify angle

Expand All @@ -19,13 +18,15 @@ public class FRU(WorldState ws, Actor primary) : BossModule(ws, primary, arena.C
private Actor? _bossP3;
private Actor? _bossP4Usurper;
private Actor? _bossP4Oracle;
private Actor? _bossP5;

public Actor? BossP1() => PrimaryActor;
public Actor? BossP2() => _bossP2;
public Actor? IceVeil() => _iceVeil;
public Actor? BossP3() => _bossP3;
public Actor? BossP4Usurper() => _bossP4Usurper;
public Actor? BossP4Oracle() => _bossP4Oracle;
public Actor? BossP5() => _bossP5;

protected override void UpdateModule()
{
Expand All @@ -36,15 +37,16 @@ protected override void UpdateModule()
_bossP3 ??= StateMachine.ActivePhaseIndex == 2 ? Enemies(OID.BossP3).FirstOrDefault() : null;
_bossP4Usurper ??= StateMachine.ActivePhaseIndex == 2 ? Enemies(OID.UsurperOfFrostP4).FirstOrDefault() : null;
_bossP4Oracle ??= StateMachine.ActivePhaseIndex == 2 ? Enemies(OID.OracleOfDarknessP4).FirstOrDefault() : null;
_bossP5 ??= StateMachine.ActivePhaseIndex == 3 ? Enemies(OID.BossP5).FirstOrDefault() : null;
}

protected override void DrawEnemies(int pcSlot, Actor pc)
{
Arena.Actor(PrimaryActor);
Arena.Actor(_bossP2);
Arena.Actor(_iceVeil);
Arena.Actor(_bossP3);
Arena.Actor(_bossP4Usurper);
Arena.Actor(_bossP4Oracle);
Arena.Actor(_bossP5);
}
}
9 changes: 9 additions & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,13 @@ public class FRUConfig() : ConfigNode()
[PropertyDisplay("P2 Banish after Light Rampant: direction to move to resolve pairs", tooltip: "Only used by AI")]
[PropertyCombo("CW", "CCW")]
public bool P2Banish2MoveCCWToStack = true;

[PropertyDisplay("P2 Intermission: clock spots (cardinals prioritize their crystals, intercardinals bait)", tooltip: "Only used by AI")]
[GroupDetails(["N", "NE", "E", "SE", "S", "SW", "W", "NW"])]
[GroupPreset("Default", [0, 2, 5, 3, 4, 6, 7, 1])]
public GroupAssignmentUnique P2IntermissionClockSpots = new() { Assignments = [0, 2, 5, 3, 4, 6, 7, 1] };

[PropertyDisplay("P3 Darkest Dance: baiter")]
[PropertyCombo("MT", "OT")]
public bool P3DarkestDanceOTBait;
}
30 changes: 30 additions & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public enum OID : uint
DragonPuddle = 0x1EBD41, // R0.500, x0 (spawn during fight), EventObj type, puddle appears when head is touched
GuardianOfEden = 0x45AE, // R115.380, x0 (spawn during fight), p5 failure state tree

BossP5 = 0x45AF, // R7.000, x0 (spawn during fight)
FulgentBladeLine = 0x1EBBF7, // R0.500, x0 (spawn during fight), EventObj type

Helper = 0x233C
}

Expand Down Expand Up @@ -249,6 +252,32 @@ public enum AID : uint
MemorysEndP4 = 40305, // OracleOfDarknessP4->self, 10.0s cast, range 100 circle, enrage
AbsoluteZeroP4 = 40245, // UsurperOfFrostP4->self, 10.0s cast, range 100 circle, enrage
ParadiseLost = 40263, // Helper->self, no cast, range 100 circle, wipe on p5 failure state
IntermissionP5Visual = 40231, // UsurperOfFrostP4->self, no cast, single-target, visual (intermission start)
IntermissionP5Start = 40232, // Helper->self, no cast, range 60 circle, stun + move players to a specific spot

// P5
AutoAttackP5 = 40114, // BossP5->self, no cast, single-target, visual (auto attack on both tanks)
AutoAttackP5AOE = 40115, // Helper->player, no cast, single-target, auto-attack

FulgentBlade = 40306, // BossP5->self, 6.0s cast, range 100 circle, raidwide + mechanic start
PathOfLightFirst = 40307, // Helper->self, 7.0s cast, range 5 width 80 rect
PathOfLightRest = 40308, // Helper->self, no cast, range 5 width 80 rect
PathOfDarknessFirst = 40118, // Helper->self, 7.0s cast, range 5 width 80 rect
PathOfDarknessRest = 40309, // Helper->self, no cast, range 5 width 80 rect

AkhMornPandora = 40310, // BossP5->self, 8.0s cast, single-target, visual (left/right stack)
AkhMornPandoraAOE1 = 40311, // Helper->players, no cast, range 4 circle, 4-man stack
AkhMornPandoraAOE2 = 40312, // Helper->players, no cast, range 4 circle, 4-man stack

ParadiseRegained = 40319, // BossP5->self, 4.0s cast, single-target, visual (mechanic start)
WingsDarkAndLightDL = 40233, // BossP5->self, 6.9+0.1s cast, single-target, visual (dark > light)
WingsDarkAndLightLD = 40313, // BossP5->self, 6.9+0.1s cast, single-target, visual (light > dark)
WingsDarkAndLightExplosion = 40320, // Helper->self, no cast, range 3 circle, tower
WingsDarkAndLightUnmitigatedExplosion = 40321, // Helper->self, no cast, range 100 circle, tower fail
WingsDarkAndLightCleaveLight = 40314, // BossP5->self, no cast, range 100 240?-degree cone on target
WingsDarkAndLightCleaveDark = 40315, // BossP5->self, no cast, range 100 240?-degree cone on target
WingsDarkAndLightTetherLight = 39879, // Helper->players, no cast, range 4 circle on farthest
WingsDarkAndLightTetherDark = 39880, // Helper->player, no cast, range 4 circle on closest
}

public enum SID : uint
Expand Down Expand Up @@ -308,4 +337,5 @@ public enum TetherID : uint
UltimateRelativitySlow = 133, // DelightsHourglass->BossP3
UltimateRelativityQuicken = 134, // DelightsHourglass->BossP3
MornAfahHPCheck = 1, // UsurperOfFrostP4->OracleOfDarknessP4
MornAfahHPFail = 2, // UsurperOfFrostP4->OracleOfDarknessP4
}
102 changes: 80 additions & 22 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ class FRUStates : StateMachineBuilder
{
private readonly FRU _module;

private static bool IsActorDead(Actor? a, bool valueIfNull) => a == null ? valueIfNull : (a.IsDeadOrDestroyed || a.HPMP.CurHP <= 1);

public FRUStates(FRU module) : base(module)
{
_module = module;
SimplePhase(0, Phase1, "P1: Fatebreaker")
.Raw.Update = () => Module.PrimaryActor.IsDeadOrDestroyed;
SimplePhase(1, Phase2, "P2: Usurper of Frost")
.SetHint(StateMachine.PhaseHint.StartWithDowntime)
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDeadOrDestroyed ?? false) || (_module.IceVeil()?.IsDeadOrDestroyed ?? false);
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDestroyed ?? false) || (_module.IceVeil()?.IsDeadOrDestroyed ?? false);
SimplePhase(2, Phase34, "P3/4: Oracle of Darkness & Both")
.SetHint(StateMachine.PhaseHint.StartWithDowntime)
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDeadOrDestroyed ?? false) && (_module.BossP3()?.IsDeadOrDestroyed ?? true) && (_module.BossP4Oracle()?.IsDeadOrDestroyed ?? true);
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP2()?.IsDestroyed ?? false) && (_module.BossP3()?.IsDestroyed ?? true) && IsActorDead(_module.BossP4Oracle(), true) && IsActorDead(_module.BossP4Usurper(), true);
SimplePhase(3, Phase5, "P5: Pandora")
.SetHint(StateMachine.PhaseHint.StartWithDowntime)
.Raw.Update = () => !Module.PrimaryActor.IsDead || (_module.BossP4Oracle()?.IsDeadOrDestroyed ?? true) && (_module.BossP5()?.IsDeadOrDestroyed ?? true);
}

private void Phase1(uint id)
Expand Down Expand Up @@ -56,6 +61,13 @@ private void Phase34(uint id)
P4CrystallizeTime(id + 0x130000, 4.6f);
P4AkhMornMornAfah(id + 0x140000, 0.1f);
P4Enrage(id + 0x150000, 2.3f);
}

private void Phase5(uint id)
{
P5Start(id, 77);
P5FulgentBlade(id + 0x10000, 5.3f);
P5ParadiseRegained(id + 0x20000, 4.2f);

SimpleState(id + 0xFF0000, 100, "???");
}
Expand Down Expand Up @@ -367,14 +379,14 @@ private void P2AbsoluteZero(uint id, float delay)
ComponentCondition<P2SwellingFrost>(id + 3, 2.3f, comp => comp.NumCasts > 0, "Knockback")
.DeactivateOnExit<P2SwellingFrost>();

ComponentCondition<P2CrystalOfLight>(id + 0x1000, 18.9f, comp => comp.ActiveActors.Any(), "Crystals appear")
.ActivateOnEnter<P2CrystalOfLight>()
ComponentCondition<P2Intermission>(id + 0x1000, 18.9f, comp => comp.CrystalsActive, "Crystals appear")
.ActivateOnEnter<P2SinboundBlizzard>()
.ActivateOnEnter<P2Intermission>()
.SetHint(StateMachine.StateHint.DowntimeEnd);
ActorCast(id + 0x1010, _module.IceVeil, AID.EndlessIceAge, 4.7f, 40, true, "Enrage")
.ActivateOnEnter<P2SinboundBlizzard>()
.ActivateOnEnter<P2HiemalStorm>()
.ActivateOnEnter<P2HiemalRay>()
.DeactivateOnExit<P2CrystalOfLight>()
.DeactivateOnExit<P2Intermission>()
.DeactivateOnExit<P2SinboundBlizzard>()
.DeactivateOnExit<P2HiemalStorm>()
.DeactivateOnExit<P2HiemalRay>();
Expand Down Expand Up @@ -454,40 +466,46 @@ private void P3Apocalypse(uint id, float delay)
ComponentCondition<P3ApocalypseDarkWater>(id + 0x12, 0.6f, comp => comp.NumStatuses >= 6)
.ActivateOnEnter<P3Apocalypse>()
.ActivateOnEnter<P3ApocalypseDarkWater>()
.ActivateOnEnter<P3ApocalypseAIWater1>()
.ExecOnExit<P3ApocalypseDarkWater>(comp => comp.ShowOrder(1));
ActorCast(id + 0x20, _module.BossP3, AID.Apocalypse, 2.6f, 4, true);
ActorCastStart(id + 0x30, _module.BossP3, AID.SpiritTaker, 2.2f, true);
ComponentCondition<P3ApocalypseDarkWater>(id + 0x31, 1.3f, comp => comp.Stacks.Count == 0, "Stack 1");
ComponentCondition<P3ApocalypseDarkWater>(id + 0x31, 1.3f, comp => comp.Stacks.Count == 0, "Stack 1")
.DeactivateOnExit<P3ApocalypseAIWater1>();
ActorCastEnd(id + 0x32, _module.BossP3, 1.7f, true)
.ActivateOnEnter<P3SpiritTaker>();
ComponentCondition<P3SpiritTaker>(id + 0x33, 0.3f, comp => comp.Spreads.Count == 0, "Jump")
.DeactivateOnExit<P3SpiritTaker>();
.ActivateOnEnter<P3ApocalypseSpiritTaker>();
ComponentCondition<SpiritTaker>(id + 0x33, 0.3f, comp => comp.Spreads.Count == 0, "Jump")
.DeactivateOnExit<SpiritTaker>();
ActorCastStart(id + 0x40, _module.BossP3, AID.ApocalypseDarkEruption, 6.2f, true)
.ExecOnEnter<P3Apocalypse>(comp => comp.Show(8.5f))
.ActivateOnEnter<P3ApocalypseDarkEruption>();
ComponentCondition<P3Apocalypse>(id + 0x41, 2.4f, comp => comp.NumCasts > 0, "Apocalypse start");
ComponentCondition<P3Apocalypse>(id + 0x41, 2.4f, comp => comp.NumCasts >= 4, "Apocalypse start");
ActorCastEnd(id + 0x42, _module.BossP3, 1.6f, true);
ComponentCondition<P3Apocalypse>(id + 0x43, 0.4f, comp => comp.NumCasts > 4);
ComponentCondition<P3Apocalypse>(id + 0x43, 0.4f, comp => comp.NumCasts >= 10);
ComponentCondition<P3ApocalypseDarkEruption>(id + 0x44, 0.7f, comp => comp.NumFinishedSpreads > 0, "Spread")
.DeactivateOnExit<P3ApocalypseDarkEruption>()
.ExecOnExit<P3ApocalypseDarkWater>(comp => comp.ShowOrder(2));
ComponentCondition<P3Apocalypse>(id + 0x45, 1.3f, comp => comp.NumCasts > 10);
ComponentCondition<P3Apocalypse>(id + 0x45, 1.3f, comp => comp.NumCasts >= 16)
.ActivateOnEnter<P3ApocalypseAIWater2>();
ActorCastStart(id + 0x50, _module.BossP3, AID.DarkestDance, 1.3f, true);
ComponentCondition<P3Apocalypse>(id + 0x51, 0.7f, comp => comp.NumCasts > 16);
ComponentCondition<P3Apocalypse>(id + 0x52, 2.0f, comp => comp.NumCasts > 22);
ComponentCondition<P3Apocalypse>(id + 0x51, 0.7f, comp => comp.NumCasts >= 22);
ComponentCondition<P3Apocalypse>(id + 0x52, 2.0f, comp => comp.NumCasts >= 28);
ComponentCondition<P3ApocalypseDarkWater>(id + 0x53, 0.5f, comp => comp.Stacks.Count == 0, "Stack 2");
ComponentCondition<P3Apocalypse>(id + 0x54, 1.5f, comp => comp.NumCasts > 28)
ComponentCondition<P3Apocalypse>(id + 0x54, 1.5f, comp => comp.NumCasts >= 34)
.ActivateOnEnter<P3DarkestDanceBait>()
.DeactivateOnExit<P3Apocalypse>();
ActorCastEnd(id + 0x55, _module.BossP3, 0.3f, true);
ComponentCondition<P3DarkestDanceBait>(id + 0x56, 0.4f, comp => comp.NumCasts > 0, "Tankbuster")
.ActivateOnEnter<P3DarkestDanceKnockback>()
.DeactivateOnExit<P3ApocalypseAIWater2>()
.DeactivateOnExit<P3DarkestDanceBait>()
.ExecOnExit<P3ApocalypseDarkWater>(comp => comp.ShowOrder(3))
.SetHint(StateMachine.StateHint.Tankbuster);
ComponentCondition<P3DarkestDanceKnockback>(id + 0x57, 2.8f, comp => comp.NumCasts > 0, "Knockback")
.ActivateOnEnter<P3ApocalypseAIWater3>()
.DeactivateOnExit<P3DarkestDanceKnockback>();
ComponentCondition<P3ApocalypseDarkWater>(id + 0x60, 4.1f, comp => comp.Stacks.Count == 0, "Stack 3")
.DeactivateOnExit<P3ApocalypseAIWater3>()
.DeactivateOnExit<P3ApocalypseDarkWater>();

P3ShockwavePulsar(id + 0x1000, 0.3f);
Expand Down Expand Up @@ -530,10 +548,10 @@ private void P4DarklitDragonsong(uint id, float delay)
ComponentCondition<P4DarklitDragonsongPathOfLight>(id + 0x21, 0.8f, comp => comp.NumCasts > 0, "Proteans")
.DeactivateOnExit<P4DarklitDragonsongPathOfLight>();
ActorCastEnd(id + 0x22, _module.BossP4Oracle, 2.2f, true)
.ActivateOnEnter<P3SpiritTaker>();
.ActivateOnEnter<DefaultSpiritTaker>();
ActorCastStartMulti(id + 0x23, _module.BossP4Usurper, [AID.HallowedWingsL, AID.HallowedWingsR], 0.1f, true);
ComponentCondition<P3SpiritTaker>(id + 0x24, 0.3f, comp => comp.Spreads.Count == 0, "Jump")
.DeactivateOnExit<P3SpiritTaker>();
ComponentCondition<SpiritTaker>(id + 0x24, 0.3f, comp => comp.Spreads.Count == 0, "Jump")
.DeactivateOnExit<SpiritTaker>();
ActorCastStart(id + 0x25, _module.BossP4Oracle, AID.SomberDance, 2.8f)
.ActivateOnEnter<P4HallowedWingsL>()
.ActivateOnEnter<P4HallowedWingsR>()
Expand Down Expand Up @@ -614,10 +632,10 @@ private void P4CrystallizeTime(uint id, float delay)
.DeactivateOnExit<P4CrystallizeTime>();
ActorCastStart(id + 0x90, _module.BossP4Oracle, AID.SpiritTaker, 0.4f);
ActorCastStart(id + 0x91, _module.BossP4Usurper, AID.CrystallizeTimeHallowedWings1, 2.2f)
.ActivateOnEnter<P3SpiritTaker>();
.ActivateOnEnter<DefaultSpiritTaker>();
ActorCastEnd(id + 0x92, _module.BossP4Oracle, 0.8f);
ComponentCondition<P3SpiritTaker>(id + 0x93, 0.3f, comp => comp.Spreads.Count == 0, "Jump")
.DeactivateOnExit<P3SpiritTaker>();
ComponentCondition<SpiritTaker>(id + 0x93, 0.3f, comp => comp.Spreads.Count == 0, "Jump")
.DeactivateOnExit<SpiritTaker>();
ComponentCondition<P4CrystallizeTimeRewind>(id + 0x94, 3.3f, comp => comp.ReturnDone, "Rewind return")
.DeactivateOnExit<P4CrystallizeTimeRewind>();
ActorCastEnd(id + 0x95, _module.BossP4Usurper, 0.3f);
Expand All @@ -632,4 +650,44 @@ private void P4Enrage(uint id, float delay)
{
ActorCast(id, _module.BossP4Usurper, AID.AbsoluteZeroP4, delay, 10, true, "Enrage");
}

private void P5Start(uint id, float delay)
{
ActorTargetable(id, _module.BossP5, true, delay, "Boss appears")
.SetHint(StateMachine.StateHint.DowntimeEnd);
}

private void P5FulgentBlade(uint id, float delay)
{
ActorCast(id, _module.BossP5, AID.FulgentBlade, delay, 6, true, "Raidwide")
.ActivateOnEnter<P5FulgentBlade>()
.SetHint(StateMachine.StateHint.Raidwide);
ComponentCondition<P5FulgentBlade>(id + 0x10, 4.1f, comp => comp.Active);
ComponentCondition<P5FulgentBlade>(id + 0x20, 7, comp => comp.NumCasts > 0, "Exaline 1");
ComponentCondition<P5FulgentBlade>(id + 0x21, 2, comp => comp.NumCasts > 1, "Exaline 2");
ComponentCondition<P5FulgentBlade>(id + 0x22, 2, comp => comp.NumCasts > 2, "Exaline 3");
ComponentCondition<P5FulgentBlade>(id + 0x23, 2, comp => comp.NumCasts > 3, "Exaline 4");
ActorCastStart(id + 0x30, _module.BossP5, AID.AkhMornPandora, 1.8f, true);
ComponentCondition<P5FulgentBlade>(id + 0x31, 0.2f, comp => comp.NumCasts > 4, "Exaline 5")
.ActivateOnEnter<P5AkhMorn>();
ComponentCondition<P5FulgentBlade>(id + 0x32, 2, comp => comp.NumCasts > 5, "Exaline 6");
ActorCastEnd(id + 0x33, _module.BossP5, 5.8f, true);
ComponentCondition<P5AkhMorn>(id + 0x34, 0.1f, comp => comp.Source == null, "Left/right stack")
.DeactivateOnExit<P5AkhMorn>()
.DeactivateOnExit<P5FulgentBlade>(); // TODO: there are still lines going, but they are far...
}

private void P5ParadiseRegained(uint id, float delay)
{
ActorCast(id, _module.BossP5, AID.ParadiseRegained, delay, 4, true)
.ActivateOnEnter<P5ParadiseRegainedTowers>(); // first tower appears ~0.9s after cast end, then every 3.5s
ActorCastMulti(id + 0x10, _module.BossP5, [AID.WingsDarkAndLightDL, AID.WingsDarkAndLightLD], 3.2f, 6.9f, true)
.ActivateOnEnter<P5ParadiseRegainedBaits>();
ComponentCondition<P5ParadiseRegainedBaits>(id + 0x20, 0.5f, comp => comp.NumCasts > 0, "Light/dark"); // first tower resolve ~0.1s earlier
ComponentCondition<P5ParadiseRegainedBaits>(id + 0x30, 3.7f, comp => comp.NumCasts > 1, "Dark/light") // second tower resolves ~1s earlier
.DeactivateOnExit<P5ParadiseRegainedBaits>();
// note: tethers resolve ~0.7s after cleave, but they won't happen if tether target dies to cleave
ComponentCondition<P5ParadiseRegainedTowers>(id + 0x40, 2.4f, comp => comp.NumCasts > 2, "Towers resolve")
.DeactivateOnExit<P5ParadiseRegainedTowers>();
}
}
Loading