Skip to content

Commit

Permalink
Merge pull request #379 from xanunderscore/dtdungeon12
Browse files Browse the repository at this point in the history
Dawntrail dungeons 1&2
  • Loading branch information
awgil committed Jul 11, 2024
2 parents 1ecab75 + 9dbf9d2 commit a13b3c7
Show file tree
Hide file tree
Showing 9 changed files with 756 additions and 3 deletions.
4 changes: 2 additions & 2 deletions BossMod/Components/BaitAway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ public override void OnUntethered(Actor source, ActorTetherInfo tether)
if (target == null)
return (null, null);

var (player, enemy) = source.Type == ActorType.Player ? (source, target) : (target, source);
if (player.Type != ActorType.Player || enemy.Type == ActorType.Player)
var (player, enemy) = source.Type is ActorType.Player or ActorType.DutySupport ? (source, target) : (target, source);
if (!(player.Type is ActorType.Player or ActorType.DutySupport) || enemy.Type == ActorType.Player)
{
ReportError($"Unexpected tether pair: {source.InstanceID:X} -> {target.InstanceID:X}");
return (null, null);
Expand Down
1 change: 0 additions & 1 deletion BossMod/Config/ModuleViewer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using BossMod.Autorotation;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
Expand Down
1 change: 1 addition & 0 deletions BossMod/Data/Actor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum ActorType : ushort
Pet = 0x202,
Chocobo = 0x203,
Enemy = 0x205,
DutySupport = 0x209,
EventNpc = 0x300,
Treasure = 0x400,
Aetheryte = 0x500,
Expand Down
189 changes: 189 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D011PrimePunutiy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
namespace BossMod.Dawntrail.Dungeon.D01Ihuykatumu.D011PrimePunutiy;

public enum OID : uint
{
Helper = 0x233C, // R0.500, x16 (spawn during fight), 523 type
Boss = 0x4190, // R7.990, x1
ProdigiousPunutiy = 0x4191, // R4.230, x0 (spawn during fight)
Punutiy = 0x4192, // R2.820, x0 (spawn during fight)
PetitPunutiy = 0x4193, // R2.115, x0 (spawn during fight)
IhuykatumuFlytrap = 0x4194, // R1.600, x0 (spawn during fight)
}

public enum AID : uint
{
PunutiyPress = 36492, // Boss->self, 5.0s cast, range 60 circle
Hydrowave = 36493, // Boss->self, 4.0s cast, range 60 30-degree cone
AddsHydrowave = 36509,
Resurface = 36494, // Boss->self, 5.0s cast, range 100 60-degree cone
Resurface2 = 36495, // Boss->self, 7.0s cast, single-target
Bury1 = 36497, // 233C->self, 4.0s cast, range 12 circle
Bury2 = 36498, // 233C->self, 4.0s cast, range 8 circle
Bury3 = 36499, // 233C->self, 4.0s cast, range 25 width 6 rect
Bury4 = 36500, // 233C->self, 4.0s cast, range 35 width 10 rect
Bury5 = 36501, // 233C->self, 4.0s cast, range 4 circle
Bury6 = 36502, // 233C->self, 4.0s cast, range 6 circle
Bury7 = 36503, // 233C->self, 4.0s cast, range 25 width 6 rect
Bury8 = 36504, // 233C->self, 4.0s cast, range 35 width 10 rect
Decay = 36505, // 4194->self, 7.0s cast, range ?-40 donut
ShoreShaker = 36514, // Boss->self, 4.0+1.0s cast, single-target
ShoreShaker1 = 36515, // 233C->self, 5.0s cast, range 10 circle
ShoreShaker2 = 36516, // 233C->self, 7.0s cast, range ?-20 donut
ShoreShaker3 = 36517, // 233C->self, 9.0s cast, range ?-30 donut
}

class PunutiyFlop(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.PunutiyPress));
class Hydrowave(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Hydrowave), new AOEShapeCone(60, 15.Degrees()));

class Bury(BossModule module) : Components.GenericAOEs(module)
{
private readonly List<(Actor Caster, AOEInstance AOE)> _activeAOEs = [];

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _activeAOEs.Select(x => x.AOE);

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
AOEShape? toAdd = (AID)spell.Action.ID switch
{
AID.Bury1 => new AOEShapeCircle(12),
AID.Bury2 => new AOEShapeCircle(8),
AID.Bury3 or AID.Bury7 => new AOEShapeRect(25, 3),
AID.Bury4 or AID.Bury8 => new AOEShapeRect(35, 5),
AID.Bury5 => new AOEShapeCircle(4),
AID.Bury6 => new AOEShapeCircle(6),
_ => null
};
if (toAdd != null)
_activeAOEs.Add((caster, new AOEInstance(toAdd, caster.Position, spell.Rotation, spell.NPCFinishAt)));
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
_activeAOEs.RemoveAll(x => x.Caster == caster);
}
}

class Resurface(BossModule module) : Components.GenericAOEs(module)
{
private AOEInstance? _aoe;

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

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if (spell.Action.ID == (uint)AID.Resurface)
_aoe = new AOEInstance(new AOEShapeCone(100, 32.Degrees()), caster.Position, spell.Rotation, spell.NPCFinishAt);
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (spell.Action.ID == (uint)AID.Resurface2)
_aoe = null;
}
}

class Decay(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Decay), new AOEShapeDonut(5, 40))
{
public override void DrawArenaForeground(int pcSlot, Actor pc)
=> Arena.Actors(Module.Enemies(OID.IhuykatumuFlytrap).Where(x => !x.IsDead), ArenaColor.Object, allowDeadAndUntargetable: true);
}

abstract class TetherBait(BossModule module, bool centerAtTarget = false) : Components.GenericBaitAway(module, default, true, centerAtTarget)
{
public override void DrawArenaForeground(int pcSlot, Actor pc)
{
base.DrawArenaForeground(pcSlot, pc);
foreach (var b in ActiveBaits)
{
if (Arena.Config.ShowOutlinesAndShadows)
Arena.AddLine(b.Source.Position, b.Target.Position, 0xFF000000, 2);
Arena.AddLine(b.Source.Position, b.Target.Position, ArenaColor.Danger);
}
}

public override void OnTethered(Actor source, ActorTetherInfo tether)
{
if (tether.ID != 17)
return;

var tar = WorldState.Actors.Find(tether.Target);
if (tar == null)
return;

OnTetherCreated(source, tar);
}

public override void OnUntethered(Actor source, ActorTetherInfo tether)
{
if (tether.ID == 17)
CurrentBaits.Clear();
}

protected abstract void OnTetherCreated(Actor source, Actor target);
}

class FlopBait(BossModule module) : TetherBait(module, true)
{
protected override void OnTetherCreated(Actor source, Actor target)
{
switch ((OID)source.OID)
{
case OID.ProdigiousPunutiy:
CurrentBaits.Add(new(source, target, new AOEShapeCircle(14)));
break;
case OID.PetitPunutiy:
CurrentBaits.Add(new(source, target, new AOEShapeCircle(6)));
break;
}
}
}

class HydroBait(BossModule module) : TetherBait(module, false)
{
protected override void OnTetherCreated(Actor source, Actor target)
{
if ((OID)source.OID == OID.Punutiy)
CurrentBaits.Add(new(source, target, new AOEShapeCone(60, 15.Degrees())));
}
}

class ShoreShaker(BossModule module) : Components.ConcentricAOEs(module, [new AOEShapeCircle(10), new AOEShapeDonut(10, 20), new AOEShapeDonut(20, 30)])
{
public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.ShoreShaker)
AddSequence(Module.Center, spell.NPCFinishAt);
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
var order = (AID)spell.Action.ID switch
{
AID.ShoreShaker1 => 0,
AID.ShoreShaker2 => 1,
AID.ShoreShaker3 => 2,
_ => -1
};
if (!AdvanceSequence(order, caster.Position, WorldState.FutureTime(2f)))
ReportError($"unexpected order {order}");
}
}

class D011PrimePunutiyStates : StateMachineBuilder
{
public D011PrimePunutiyStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<PunutiyFlop>()
.ActivateOnEnter<Hydrowave>()
.ActivateOnEnter<Resurface>()
.ActivateOnEnter<Bury>()
.ActivateOnEnter<Decay>()
.ActivateOnEnter<FlopBait>()
.ActivateOnEnter<HydroBait>()
.ActivateOnEnter<ShoreShaker>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "xan", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 826, NameID = 12723)]
public class D011PrimePunutiy(WorldState ws, Actor primary) : BossModule(ws, primary, new(35, -95), new ArenaBoundsSquare(20));
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// there is nothing here. boss 2 can trivially be solved by AI
130 changes: 130 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
namespace BossMod.Dawntrail.Dungeon.D01Ihuykatumu.D013Apollyon;

public enum OID : uint
{
Helper = 0x233C, // R0.500, x?, 523 type
Boss = 0x4165, // R7.000, x?
LightningAOE = 0x1EBA21, // R0.500, x?, EventObj type
Whirlwind = 0x416C, // R1.000, x?
}

public enum AID : uint
{
RazorZephyr = 36340, // 4165->self, 4.0s cast, range 50 width 12 rect
BladeST = 36347, // 4165->none, 4.5s cast, single-target
HighWind = 36341, // 4165->self, 5.0s cast, range 60 circle
BladesOfFamine = 36346, // 233C->self, 3.0s cast, range 50 width 12 rect
LevinsickleSpark = 36349, // 233C->location, 5.0s cast, range 4 circle
Levinsickle = 36350, // 233C->location, 5.0s cast, range 4 circle
WingOfLightning = 36351, // 233C->self, 8.0s cast, range 40 ?-degree cone
ThunderIII = 36353, // 233C->player, 5.0s cast, range 6 circle
BladeAOE = 36357, // 233C->none, 5.0s cast, range 6 circle
WindSickle = 36358, // 233C->self, 4.0s cast, range ?-60 donut
RazorStorm = 36355, // 4165->self, 5.0s cast, range 40 width 40 rect
Windwhistle = 36359, // 4165->self, 4.0s cast, single-target
Cuttingwind = 36360, // 233C->self, no cast, range 72 width 8 rect
}

class RazorZephyr(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.RazorZephyr), new AOEShapeRect(50, 6));
class Blade(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.BladeST));
class HighWind(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.HighWind));
class BladesOfFamine(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BladesOfFamine), new AOEShapeRect(50, 6));
class WingOfLightning(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WingOfLightning), new AOEShapeCone(40, 22.5f.Degrees()), maxCasts: 8);
class LightningHelper(BossModule module) : Components.PersistentVoidzone(module, 4, m => m.Enemies(OID.LightningAOE));
class ThunderIII(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.ThunderIII), 6);
class BladeAOE(BossModule module) : Components.BaitAwayCast(module, ActionID.MakeSpell(AID.BladeAOE), new AOEShapeCircle(6), centerAtTarget: true);
class WindSickle(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WindSickle), new AOEShapeDonut(6, 60));
class RazorStorm(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.RazorStorm), new AOEShapeRect(40, 20));
class Levinsickle(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Levinsickle), 4);
class LevinsickleSpark(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.LevinsickleSpark), 4);

// first aoe is 10 seconds after windwhistle
// rest are 8 seconds after previous
class Whirlwind(BossModule module) : Components.GenericAOEs(module)
{
private int _activations;
private DateTime _nextActivation;

private static readonly List<Angle> Rotations = [0.Degrees(), 45.Degrees(), 90.Degrees(), 135.Degrees()];

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.Windwhistle)
_nextActivation = WorldState.FutureTime(10);
}

public override void DrawArenaBackground(int pcSlot, Actor pc)
{
foreach (var c in ActiveAOEs(pcSlot, pc))
{
c.Shape.Draw(Arena, c.Origin, c.Rotation, c.Color);
c.Shape.Outline(Arena, c.Origin, c.Rotation, ArenaColor.AOE);
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID == AID.Cuttingwind)
{
_activations += 1;
_nextActivation = WorldState.FutureTime(8);
}
}

public override void OnActorDestroyed(Actor actor)
{
_activations = 0;
_nextActivation = default;
}

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_activations >= 12)
yield break;

var whirlwind = Module.Enemies(OID.Whirlwind).FirstOrDefault();
if (whirlwind == null)
yield break;

var whirlyHelper = Module.Enemies(OID.Helper).FirstOrDefault(x => x.NameID == 12715);
if (whirlyHelper == null)
yield break;

foreach (var angle in Rotations)
{
yield return new AOEInstance(new AOEShapeRect(72, 4, 72), whirlwind.Position, angle, _nextActivation, Shade(_nextActivation), _nextActivation < WorldState.FutureTime(4));
}
}

private uint Shade(DateTime activation)
{
var clampedETA = Math.Clamp((activation - WorldState.CurrentTime).TotalSeconds, 0, 4);
var opacity = 1 - clampedETA / 4;
var alpha = (uint)(opacity * 96) + 32;
return 0x008080 + alpha * 0x1000000;
}
}

class D013ApollyonStates : StateMachineBuilder
{
public D013ApollyonStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<RazorZephyr>()
.ActivateOnEnter<Blade>()
.ActivateOnEnter<HighWind>()
.ActivateOnEnter<BladesOfFamine>()
.ActivateOnEnter<WingOfLightning>()
.ActivateOnEnter<LightningHelper>()
.ActivateOnEnter<ThunderIII>()
.ActivateOnEnter<BladeAOE>()
.ActivateOnEnter<WindSickle>()
.ActivateOnEnter<RazorStorm>()
.ActivateOnEnter<Levinsickle>()
.ActivateOnEnter<LevinsickleSpark>()
.ActivateOnEnter<Whirlwind>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "xan", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 826, NameID = 12711)]
public class D013Apollyon(WorldState ws, Actor primary) : BossModule(ws, primary, new(-107, 265), new ArenaBoundsCircle(20));
Loading

0 comments on commit a13b3c7

Please sign in to comment.