Skip to content

Commit

Permalink
Full Mount Rokkon Support
Browse files Browse the repository at this point in the history
  • Loading branch information
LTS-FFXIV committed May 6, 2024
1 parent f4a1fb0 commit 34ef37c
Show file tree
Hide file tree
Showing 20 changed files with 1,537 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using BossMod.Endwalker.Alliance.A32Llymlaen;

namespace BossMod.Endwalker.Variant.V01SS.V011Geryon;
namespace BossMod.Endwalker.Variant.V01SS.V011Geryon;

public enum OID : uint
{
Expand Down
127 changes: 127 additions & 0 deletions BossMod/Modules/Endwalker/Variant/V02MR/V021Yozakura/V021Yozakura.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
namespace BossMod.Endwalker.Variant.V02MR.V021Yozakura;

class ArtOfTheFireblossom(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ArtOfTheFireblossom), new AOEShapeCircle(9));
class ArtOfTheWindblossom(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ArtOfTheWindblossom), new AOEShapeDonut(5, 60));
class KugeRantsui(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.KugeRantsui));
class OkaRanman(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.OkaRanman));
//Temporary below, ninja mechanics are basically all previs dependent, timers are too short to be super useful

class SealOfTheFireblossom1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SealOfTheFireblossom1), new AOEShapeCircle(9));
class SealOfTheWindblossom1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SealOfTheWindblossom1), new AOEShapeDonut(5, 60));
class SealOfTheRainblossom1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SealOfTheRainblossom1), new AOEShapeCone(70, 22.5f.Degrees()));
class SealOfTheLevinblossom1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SealOfTheLevinblossom1), new AOEShapeCone(70, 22.5f.Degrees()));

class SeasonOfFire(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SeasonOfFire), new AOEShapeRect(46, 2.5f));
class SeasonOfWater(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SeasonOfWater), new AOEShapeRect(46, 2.5f));
class SeasonOfLightning(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SeasonOfLightning), new AOEShapeCone(70, 22.5f.Degrees()));
class SeasonOfEarth(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SeasonOfEarth), new AOEShapeCone(70, 22.5f.Degrees()));

//Left Windy
class WindblossomWhirl2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WindblossomWhirl2), new AOEShapeDonut(5.5f, 60));
class WindblossomWhirl3(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WindblossomWhirl3), new AOEShapeDonut(5.5f, 60));
class LevinblossomStrike2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.LevinblossomStrike2), 3);
class DriftingPetals(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.DriftingPetals), 15, stopAtWall: true);

//Left Rainy
class MudrainAOE(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.MudrainAOE), 5); //persistent AOE, needs to be handled differently
class IcebloomRest(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.IcebloomRest), 6);
class ShadowflightAOE(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ShadowflightAOE), new AOEShapeRect(10, 3));
class MudPieAOE(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.MudPieAOE), new AOEShapeRect(60, 3));

//Middle Rope Pulled
class FireblossomFlare2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.FireblossomFlare2), 6);
class ArtOfTheFluff1(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.ArtOfTheFluff1));
class ArtOfTheFluff2(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.ArtOfTheFluff2));

//Middle Rope Unpulled
class TatamiGaeshiAOE(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.RockRootArrangementVisual), new AOEShapeCircle(4));
class LevinblossomLance(BossModule module) : Components.GenericRotatingAOE(module)
{
private Angle _increment;
private Angle _rotation;
private DateTime _activation;

private static readonly AOEShapeRect _shape = new AOEShapeRect(60, 3.5f, 60);

public override void OnEventIcon(Actor actor, uint iconID)
{
var increment = (IconID)iconID switch
{
IconID.RotateCW => -22.5f.Degrees(),
IconID.RotateCCW => 22.5f.Degrees(),
_ => default
};
if (increment != default)
{
_increment = increment;
InitIfReady(actor);
}
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.LevinblossomLanceFirst)
{
_rotation = spell.Rotation;
_activation = spell.NPCFinishAt;
}
if (_rotation != default)
InitIfReady(caster);
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.LevinblossomLanceFirst or AID.LevinblossomLanceRest)
AdvanceSequence(0, WorldState.CurrentTime);
}

private void InitIfReady(Actor source)
{
if (_rotation != default && _increment != default)
{
Sequences.Add(new(_shape, source.Position, _rotation, _increment, _activation, 1, 5));
_rotation = default;
_increment = default;
}
}
}

//Right No Dogu
class RootArrangement : Components.StandardChasingAOEs
{
public RootArrangement(BossModule module) : base(module, new AOEShapeCircle(4), ActionID.MakeSpell(AID.RockRootArrangementFirst), ActionID.MakeSpell(AID.RockRootArrangementRest), 4, 1, 4)
{
ExcludedTargets = Raid.WithSlot(true).Mask();
}

public override void OnEventIcon(Actor actor, uint iconID)
{
if (iconID == (uint)IconID.RootArrangement)
ExcludedTargets.Clear(Raid.FindSlot(actor.InstanceID));
}
}

//Right Dogu

class Witherwind(BossModule module) : Components.GenericAOEs(module)
{
private IReadOnlyList<Actor> _spirits = module.Enemies(OID.AutumnalTempest);

private static readonly AOEShapeCircle _shape = new(3);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _spirits.Where(actor => !actor.IsDead).Select(b => new AOEInstance(_shape, b.Position));
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "CombatReborn Team", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 945, NameID = 12325)]
public class V021Yozakura(WorldState ws, Actor primary) : BossModule(ws, primary, new ArenaBoundsSquare(new(-775, 16), 20))
{
protected override void UpdateModule()
{
if (Enemies(OID.Boss).Any(e => e.Position.AlmostEqual(new(-775, 16), 50)))
Arena.Bounds = new ArenaBoundsSquare(new(-775, 16), 20);
if (Enemies(OID.Boss).Any(e => e.Position.AlmostEqual(new(737, 220), 50)))
Arena.Bounds = new ArenaBoundsSquare(new(737, 220), 20);
if (Enemies(OID.Boss).Any(e => e.Position.AlmostEqual(new(47, 93), 50)))
Arena.Bounds = new ArenaBoundsSquare(new(47, 93), 20);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
namespace BossMod.Endwalker.Variant.V02MR.V021Yozakura;

public enum OID : uint
{
Boss = 0x3EDB, // R3.450, x1
Helper = 0x233C, // R0.500, x13, 523 type
Helper2 = 0x3F53, // R0.500, x4
ShishuYamabiko = 0x3F00, // R0.800, x1
MirroredYozakura = 0x3EDC, // R3.450, x4
MudBubble = 0x3EDD, // R4.000, x0 (spawn during fight)
Kuromaru = 0x3EDF, // R0.400, x1 (spawn during fight)
Shiromaru = 0x3EE0, // R0.400, x1 (spawn during fight)
Shibamaru = 0x3EDE, // R0.400, x1 (spawn during fight)
AccursedSeedling = 0x3EE1, // R0.750, x0 (spawn during fight)
AutumnalTempest = 0x3EE3, // R0.800, x0 (spawn during fight)
}

public enum AID : uint
{
AutoAttack = 6497, // Boss->player, no cast, single-target
GloryNeverlasting = 33705, // Boss->player, 5.0s cast, single-target //tankbuster

ArtOfTheFireblossom = 33640, // Boss->self, 3.0s cast, range 9 circle
ArtOfTheWindblossom = 33641, // Boss->self, 5.0s cast, range 6-60 donut

KugeRantsui = 33706, // Boss->self, 5.0s cast, range 60 circle //raidwide
OkaRanman = 33646, // Boss->self, 5.0s cast, range 60 circle //raidwide

SealOfRiotousBloom = 33652, // Boss->self, 5.0s cast, single-target

SealOfTheWindblossom1 = 33654, // Helper->self, 2.0s cast, range 6-60 donut //Donut AoE centered on Yozakura
SealOfTheWindblossom2 = 33659, // Boss->location, no cast, single-target

SealOfTheFireblossom1 = 33653, // Helper->self, 2.0s cast, range 9 circle //Point-blank AoE centered on Yozakura
SealOfTheFireblossom2 = 33658, // Boss->location, no cast, single-target

SealOfTheRainblossom1 = 33655, // Helper->self, 2.0s cast, range 70 45-degree cone //Four cone AoEs from Yozakura's hitbox in the intercardinal directions
SealOfTheRainblossom2 = 33661, // Boss->location, no cast, single-target

SealOfTheLevinblossom1 = 33656, // Helper->self, 1.8s cast, range 70 45-degree cone //Four cone AoEs from Yozakura's hitbox in the cardinal directions
SealOfTheLevinblossom2 = 33660, // Boss->location, no cast, single-target

SealOfTheFleeting = 33657, // Boss->self, 3.0s cast, single-target //Yozakura tethers to the petal piles

SeasonsOfTheFleeting1 = 33665, // Boss->self, 10.0s cast, single-target //telegraph four sequential AoEs

SeasonsOfTheFleeting2 = 33666, // Boss->self, no cast, single-target
FireAndWaterVisual = 33667, // Helper->self, 2.0s cast, range 46 width 5 rect
EarthAndLightningVisual = 33668, // Helper->self, 2.0s cast, range 60 ?-degree cone

SeasonOfFire = 33669, // Helper->self, 0.8s cast, range 46 width 5 rect
SeasonOfWater = 33670, // Helper->self, 0.8s cast, range 46 width 5 rect
SeasonOfLightning = 33671, // Helper->self, 0.8s cast, range 70 ?-degree cone
SeasonOfEarth = 33672, // Helper->self, 0.8s cast, range 70 ?-degree cone

UnknownAbility = 34322, // Boss->location, no cast, single-target

//Left Windy
WindblossomWhirl1 = 33679, // Boss->self, 3.0s cast, single-target
WindblossomWhirl2 = 33680, // Helper->self, 5.0s cast, range ?-60 donut
WindblossomWhirl3 = 34544, // Helper->self, 3.0s cast, range 5.5-60 donut
LevinblossomStrike1 = 33681, // Boss->self, 2.3s cast, single-target
LevinblossomStrike2 = 33682, // Helper->location, 3.0s cast, range 3 circle
DriftingPetals = 33683, // Boss->self, 5.0s cast, range 60 circle //knockback

//Left Rainy
Bunshin = 33662, // Boss->self, 5.0s cast, single-target
Shadowflight = 33663, // Boss->self, 3.0s cast, single-target
ShadowflightAOE = 33664, // MirroredYozakura->self, 2.5s cast, range 10 width 6 rect
Mudrain = 33673, // Boss->self, 3.0s cast, single-target
MudrainAOE = 33674, // Helper->location, 3.8s cast, range 5 circle
IcebloomFirst = 33675, // Boss->self, 3.0s cast, single-target
IcebloomRest = 33676, // Helper->location, 3.0s cast, range 6 circle
MudPie = 33677, // Boss->self, 3.0s cast, single-target
MudPieAOE = 33678, // MudBubble->self, 4.0s cast, range 60 width 6 rect

//Middle Rope Pulled
ArtOfTheFluff1 = 33693, // Shibamaru/Kuromaru->self, 6.5s cast, range 60 circle //gaze
ArtOfTheFluff2 = 33694, // Shiromaru->self, 6.5s cast, range 60 circle //gaze

FireblossomFlare1 = 33695, // Boss->self, 3.0s cast, single-target
FireblossomFlare2 = 33696, // Helper->location, 3.0s cast, range 6 circle

DondenGaeshi = 33692, // Boss->self, 3.0s cast, single-target // Indicates platforms
SilentWhistle = 33691, // Boss->self, 3.0s cast, single-target //Dog summons

//Middle Rope Unpulled
LevinblossomLance1 = 33687, // Boss->self, 5.0s cast, single-target
LevinblossomLance2 = 33688, // Boss->self, 5.0s cast, single-target
LevinblossomLanceFirst = 33689, // Helper->self, 5.8s cast, range 60 width 7 rect
LevinblossomLanceRest = 33690, // Helper->self, no cast, range 60 width 7 rect

TatamiTrap = 33684, // Boss->self, 3.0s cast, single-target
TatamiGaeshi = 33685, // Boss->self, 3.0s cast, single-target
TatamiGaeshiAOE = 33686, // Helper->self, 3.8s cast, range 40 width 10 rect

//Right No Dogu
RockMebuki = 33697, // Boss->self, 3.0s cast, single-target
RockRootArrangementVisual = 33700, // Boss->self, 5.0s cast, single-target
RockRootArrangementFirst = 33701, // Helper->location, 3.0s cast, range 4 circle
RockRootArrangementRest = 33702, // Helper->location, no cast, range 4 circle

//Right Dogu
Witherwind = 33703, // Boss->self, 3.0s cast, single-target

}
public enum SID : uint
{
SeasonsOfTheFleeting = 3623, // none->Boss, extra=0x0
Unknown = 2056, // none->3EE1, extra=0x243
}

public enum IconID : uint
{
Tankbuster = 218, // player
Icon374 = 374, // Shibamaru/Shiromaru/Kuromaru
RotateCW = 167, // Boss
RotateCCW = 168, // Boss
RootArrangement = 197, // player
}

public enum TetherID : uint
{
Tether_6 = 6, // Helper2->Boss
Tether_4 = 4, // Helper2->Boss
Tether_3 = 3, // Helper2->Boss
Tether_5 = 5, // Helper2->Boss
Tether_79 = 79, // Helper2->Boss
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace BossMod.Endwalker.Variant.V02MR.V021Yozakura;

class V021YozakuraStates : StateMachineBuilder
{
public V021YozakuraStates(BossModule module) : base(module)
{
TrivialPhase()
//Right No Dogu
.ActivateOnEnter<RootArrangement>()
//Right Dogu
.ActivateOnEnter<Witherwind>()
//Left Windy
.ActivateOnEnter<WindblossomWhirl2>()
.ActivateOnEnter<WindblossomWhirl3>()
.ActivateOnEnter<LevinblossomStrike2>()
.ActivateOnEnter<DriftingPetals>()
//Left Rainy
.ActivateOnEnter<MudrainAOE>()
.ActivateOnEnter<IcebloomRest>()
.ActivateOnEnter<ShadowflightAOE>()
.ActivateOnEnter<MudPieAOE>()
//Middle Rope Pulled
.ActivateOnEnter<FireblossomFlare2>()
.ActivateOnEnter<ArtOfTheFluff1>()
.ActivateOnEnter<ArtOfTheFluff2>()
//Middle Rope Unpulled
.ActivateOnEnter<LevinblossomLance>()
.ActivateOnEnter<TatamiGaeshiAOE>()
//Standard
.ActivateOnEnter<KugeRantsui>()
.ActivateOnEnter<OkaRanman>()
.ActivateOnEnter<SealOfTheFireblossom1>()
.ActivateOnEnter<SealOfTheWindblossom1>()
.ActivateOnEnter<SealOfTheRainblossom1>()
.ActivateOnEnter<SealOfTheLevinblossom1>()
.ActivateOnEnter<SeasonOfFire>()
.ActivateOnEnter<SeasonOfWater>()
.ActivateOnEnter<SeasonOfLightning>()
.ActivateOnEnter<SeasonOfEarth>()
.ActivateOnEnter<ArtOfTheFireblossom>()
.ActivateOnEnter<ArtOfTheWindblossom>();
}
}
83 changes: 83 additions & 0 deletions BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/AzureAuspice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
namespace BossMod.Endwalker.Variant.V02MR.V022Moko;

// note: each initial line sends out two 'exaflares' to the left & right
// each subsequent exaflare moves by distance 5, and happen approximately 2s apart
// each wave is 5 subsequent lines, except for two horizontal ones that go towards edges - they only have 1 line - meaning there's a total 32 'rest' casts
class Upwell(BossModule module) : Components.GenericAOEs(module)
{
private class LineSequence
{
public WPos NextOrigin;
public WDir Advance;
public Angle Rotation;
public DateTime NextActivation;
public AOEShapeRect? NextShape; // wide for first line, null for first line mirror, narrow for remaining lines
}

private List<LineSequence> _lines = new();

private static readonly AOEShapeRect _shapeWide = new(30, 5, 30);
private static readonly AOEShapeRect _shapeNarrow = new(30, 2.5f, 30);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
// TODO: think about imminent/future color/risk, esp for overlapping lines
var imminentDeadline = WorldState.FutureTime(5);
foreach (var l in _lines)
if (l.NextShape != null && l.NextActivation <= imminentDeadline)
yield return new(l.NextShape, l.NextOrigin, l.Rotation, l.NextActivation);
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.UpwellFirst)
{
var advance = spell.Rotation.ToDirection().OrthoR() * 5;
_lines.Add(new() { NextOrigin = caster.Position, Advance = advance, Rotation = spell.Rotation, NextActivation = spell.NPCFinishAt, NextShape = _shapeWide });
_lines.Add(new() { NextOrigin = caster.Position, Advance = -advance, Rotation = (spell.Rotation + 180.Degrees()).Normalized(), NextActivation = spell.NPCFinishAt });
}
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.UpwellFirst)
{
++NumCasts;
var index = _lines.FindIndex(l => l.NextOrigin.AlmostEqual(caster.Position, 1) && l.NextShape == _shapeWide && l.Rotation.AlmostEqual(spell.Rotation, 0.1f));
if (index < 0 || index + 1 >= _lines.Count)
{
ReportError($"Unexpected exaline end");
}
else
{
Advance(_lines[index]);
Advance(_lines[index + 1]);
}
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.UpwellRest)
{
++NumCasts;
var index = _lines.FindIndex(l => l.NextOrigin.AlmostEqual(caster.Position, 1) && l.NextShape == _shapeNarrow && l.Rotation.AlmostEqual(caster.Rotation, 0.1f));
if (index < 0)
{
ReportError($"Unexpected exaline @ {caster.Position} / {caster.Rotation}");
}
else
{
Advance(_lines[index]);
}
}
}

private void Advance(LineSequence line)
{
line.NextOrigin += line.Advance;
line.NextActivation = WorldState.FutureTime(2);
var offset = (line.NextOrigin - Module.Bounds.Center).Abs();
line.NextShape = offset.X < 19 && offset.Z < 19 ? _shapeNarrow : null;
}
}
Loading

0 comments on commit 34ef37c

Please sign in to comment.