Skip to content

Commit

Permalink
Autoduty fun: skydeep cenote
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Aug 21, 2024
1 parent bd5ca61 commit 400e926
Show file tree
Hide file tree
Showing 4 changed files with 469 additions and 1 deletion.
2 changes: 1 addition & 1 deletion BossMod/Components/BaitAway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class GenericBaitAway(BossModule module, ActionID aid = default, bool alw
{
public record struct Bait(Actor Source, Actor Target, AOEShape Shape, DateTime Activation = default)
{
public readonly Angle Rotation => Angle.FromDirection(Target.Position - Source.Position);
public readonly Angle Rotation => Source != Target ? Angle.FromDirection(Target.Position - Source.Position) : Source.Rotation;
}

public bool AlwaysDrawOtherBaits = alwaysDrawOtherBaits; // if false, other baits are drawn only if they are clipping a player
Expand Down
140 changes: 140 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D03SkydeepCenote/D031FeatherRay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
namespace BossMod.Dawntrail.Dungeon.D03SkydeepCenote.D031FeatherRay;

public enum OID : uint
{
Boss = 0x41D3, // R5.000, x1
AiryBubble = 0x41D4, // R1.100-2.200, x36
Helper = 0x233C, // R0.500, x5, Helper type
}

public enum AID : uint
{
AutoAttack = 872, // Boss->player, no cast, single-target
Immersion = 36739, // Boss->self, 5.0s cast, range 24 circle, raidwide
TroublesomeTail = 36727, // Boss->self, 4.0s cast, range 24 circle, raidwide + mirror debuff
WorrisomeWave = 36728, // Boss->self, 4.0s cast, range 24 30-degree cone
WorrisomeWaveNuisance = 36729, // Helper->self, no cast, range 24 30-degree cone
HydroRing = 36733, // Boss->self, 5.0s cast, range 12-24 donut, leaves persistent voidzone
BlowingBubbles = 36732, // Boss->self, 3.0s cast, single-target, visual (start spawning small bubbles from boss)
BubbleBomb = 36735, // Boss->self, 3.0s cast, single-target, visual (spawn large bubbles)
RollingCurrent = 36737, // Boss->self, 5.0s cast, single-target, visual (move bubbles)
RollingCurrentAOE = 38185, // Helper->self, 5.0s cast, range 68 width 32 rect, knock-forward 8 on bubbles
Burst = 36738, // AiryBubble->self, 1.5s cast, range 6 circle
TroubleBubbles = 38787, // Boss->self, 3.0s cast, single-target, visual (start spawning small bubbles from players)
Pop = 36734, // AiryBubble->player, no cast, single-target, damage if bubble is touched
}

public enum IconID : uint
{
Nuisance = 514, // player
}

class Immersion(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Immersion));
class TroublesomeTail(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.TroublesomeTail));
class WorrisomeWave(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WorrisomeWave), new AOEShapeCone(24, 15.Degrees()));

class WorrisomeWaveNuisance(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCone(24, 15.Degrees()), (uint)IconID.Nuisance, ActionID.MakeSpell(AID.WorrisomeWaveNuisance), 5.4f)
{
public override Actor? BaitSource(Actor target) => target;
}

class HydroRing(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.HydroRing))
{
private AOEInstance? _aoe;

private static readonly AOEShapeDonut _shape = new(12, 24);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe);

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if (spell.Action == WatchedAction)
_aoe = new(_shape, caster.Position, spell.Rotation, Module.CastFinishAt(spell));
}

public override void OnEventEnvControl(byte index, uint state)
{
if (index == 19 && state == 0x00080004)
_aoe = null;
}
}

class AiryBubbles(BossModule module) : Components.GenericAOEs(module)
{
private readonly List<Actor> _bubbles = [];

private static readonly AOEShapeCircle _shape = new(1.1f);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _bubbles.Select(b => new AOEInstance(_shape, b.Position));

public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id)
{
if ((OID)actor.OID != OID.AiryBubble || actor.HitboxRadius > _shape.Radius + 0.01f)
return;
switch (id)
{
case 0x1E46:
_bubbles.Add(actor);
break;
case 0x1E3C:
_bubbles.Remove(actor);
break;
}
}
}

class BubbleBomb(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.Burst))
{
private readonly List<AOEInstance> _aoes = [];
private bool _directionKnown;

private static readonly AOEShapeCircle _shape = new(6);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _directionKnown ? _aoes : [];

public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id)
{
if ((OID)actor.OID == OID.AiryBubble && actor.HitboxRadius > 2.1f && id == 0x1E46)
_aoes.Add(new(_shape, actor.Position, default, WorldState.FutureTime(10.6f)));
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.RollingCurrentAOE)
{
var offset = 8 * spell.Rotation.ToDirection();
foreach (ref var aoe in _aoes.AsSpan())
aoe.Origin += offset;
_directionKnown = true;
}
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (spell.Action == WatchedAction)
{
_directionKnown = false;
var count = _aoes.RemoveAll(aoe => aoe.Origin.AlmostEqual(caster.Position, 1));
if (count != 1)
ReportError($"Failed to find aoe @ {caster.Position}");
}
}
}

class D031FeatherRayStates : StateMachineBuilder
{
public D031FeatherRayStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<Immersion>()
.ActivateOnEnter<TroublesomeTail>()
.ActivateOnEnter<WorrisomeWave>()
.ActivateOnEnter<WorrisomeWaveNuisance>()
.ActivateOnEnter<HydroRing>()
.ActivateOnEnter<AiryBubbles>()
.ActivateOnEnter<BubbleBomb>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 829, NameID = 12755)]
public class D031FeatherRay(WorldState ws, Actor primary) : BossModule(ws, primary, new(-105, -160), new ArenaBoundsSquare(15));
98 changes: 98 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D03SkydeepCenote/D032Firearms.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
namespace BossMod.Dawntrail.Dungeon.D03SkydeepCenote.D032Firearms;

public enum OID : uint
{
Boss = 0x4184, // R4.620, x1
Helper = 0x233C, // R0.500, x20, Helper type
}

public enum AID : uint
{
AutoAttack = 872, // Boss->player, no cast, single-target
Teleport = 36451, // Boss->location, no cast, single-target
DynamicDominance = 36448, // Boss->self, 5.0s cast, range 70 circle, raidwide
MirrorManeuver = 39139, // Boss->self, 3.0s cast, single-target, visual (mirrors & orbs)
ThunderlightBurst = 36443, // Boss->self, 8.0s cast, single-target, visual (laser)
ThunderlightBurstAOERect1 = 38581, // Helper->self, 8.2s cast, range 42 width 8 rect
ThunderlightBurstAOERect2 = 38582, // Helper->self, 8.2s cast, range 49 width 8 rect
ThunderlightBurstAOERect3 = 38583, // Helper->self, 8.2s cast, range 35 width 8 rect
ThunderlightBurstAOERect4 = 38584, // Helper->self, 8.2s cast, range 36 width 8 rect
ThunderlightBurstAOECircle = 36445, // Helper->self, 10.9s cast, range 35 circle
AncientArtillery = 36442, // Boss->self, 3.0s cast, single-target, visual (show expanding square)
EmergentArtillery = 39000, // Boss->self, 3.0s cast, single-target, visual (expand & explode square)
ArtilleryAOE1 = 38660, // Helper->self, 8.5s cast, range 10 width 10 rect
ArtilleryAOE2 = 38661, // Helper->self, 8.5s cast, range 10 width 10 rect
ArtilleryAOE3 = 38662, // Helper->self, 8.5s cast, range 10 width 10 rect
ArtilleryAOE4 = 38663, // Helper->self, 8.5s cast, range 10 width 10 rect
Pummel = 36447, // Boss->player, 5.0s cast, single-target, tankbuster
ThunderlightFlurry = 36450, // Helper->player, 5.0s cast, range 6 circle spread
}

public enum IconID : uint
{
Pummel = 218, // player
ThunderlightFlurry = 139, // player
}

class DynamicDominance(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.DynamicDominance));
class ThunderlightBurstRect1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ThunderlightBurstAOERect1), new AOEShapeRect(42, 4));
class ThunderlightBurstRect2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ThunderlightBurstAOERect2), new AOEShapeRect(49, 4));
class ThunderlightBurstRect3(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ThunderlightBurstAOERect3), new AOEShapeRect(35, 4));
class ThunderlightBurstRect4(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ThunderlightBurstAOERect4), new AOEShapeRect(36, 4));
class ThunderlightBurstCircle(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ThunderlightBurstAOECircle), new AOEShapeCircle(35));

class Artillery(BossModule module) : Components.GenericAOEs(module)
{
public readonly List<AOEInstance> AOEs = [];

private static readonly AOEShapeRect _shape = new(5, 5, 5);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => AOEs;

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.ArtilleryAOE1 or AID.ArtilleryAOE2 or AID.ArtilleryAOE3 or AID.ArtilleryAOE4)
AOEs.Add(new(_shape, caster.Position, spell.Rotation, Module.CastFinishAt(spell)));
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.ArtilleryAOE1 or AID.ArtilleryAOE2 or AID.ArtilleryAOE3 or AID.ArtilleryAOE4)
AOEs.RemoveAll(aoe => aoe.Origin.AlmostEqual(caster.Position, 1));
}

}

class Pummel(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.Pummel));

class ThunderlightFlurry(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.ThunderlightFlurry), 6)
{
private readonly Artillery? _artillery = module.FindComponent<Artillery>();

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
// start adding hints only after overlapping artillery is resolved
if (_artillery == null || _artillery.AOEs.Count == 0)
base.AddAIHints(slot, actor, assignment, hints);
}
}

class D032FirearmsStates : StateMachineBuilder
{
public D032FirearmsStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<DynamicDominance>()
.ActivateOnEnter<ThunderlightBurstRect1>()
.ActivateOnEnter<ThunderlightBurstRect2>()
.ActivateOnEnter<ThunderlightBurstRect3>()
.ActivateOnEnter<ThunderlightBurstRect4>()
.ActivateOnEnter<ThunderlightBurstCircle>()
.ActivateOnEnter<Artillery>()
.ActivateOnEnter<Pummel>()
.ActivateOnEnter<ThunderlightFlurry>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 829, NameID = 12888)]
public class D032Firearms(WorldState ws, Actor primary) : BossModule(ws, primary, new(-85, -155), new ArenaBoundsSquare(20));
Loading

0 comments on commit 400e926

Please sign in to comment.