Skip to content

Commit

Permalink
Merge branch 'lab' of https://github.com/Akechi-kun/ffxiv_bossmod int…
Browse files Browse the repository at this point in the history
…o lab
  • Loading branch information
Akechi-kun committed Oct 25, 2024
2 parents 207e28e + 3b92056 commit 7b3194f
Show file tree
Hide file tree
Showing 26 changed files with 457 additions and 36 deletions.
30 changes: 27 additions & 3 deletions BossMod/Autorotation/MiscAI/StayCloseToTarget.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
namespace BossMod.Autorotation.MiscAI;
using System.Globalization;

namespace BossMod.Autorotation.MiscAI;

public sealed class StayCloseToTarget(RotationModuleManager manager, Actor player) : RotationModule(manager, player)
{
public static RotationModuleDefinition Definition() => new("Misc AI: Stay within 10m of target", "Module for use by AutoDuty preset.", "AI Behaviours", "veyn", RotationModuleQuality.Basic, new(~0ul), 1000);

public enum Tracks
{
Range
}

public enum RangeDefinition
{
}

public static RotationModuleDefinition Definition()
{
RotationModuleDefinition def = new("Misc AI: Stay within range of target", "Module for use by AutoDuty preset.", "Misc", "veyn", RotationModuleQuality.Basic, new(~0ul), 1000);

var configRef = def.Define(Tracks.Range).As<RangeDefinition>("range");

for (float f = 1; f <= 30f; f = MathF.Round(f + 0.1f, 1))
{
configRef.AddOption((RangeDefinition)(f * 10f - 10f), f.ToString(CultureInfo.InvariantCulture));
}

return def;
}

public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving)
{
if (primaryTarget != null)
Hints.GoalZones.Add(Hints.GoalSingleTarget(primaryTarget.Position, 10, 0.5f));
Hints.GoalZones.Add(Hints.GoalSingleTarget(primaryTarget.Position, (strategy.Option(Tracks.Range).Value.Option + 10f) / 10f + primaryTarget.HitboxRadius, 0.5f));
}
}
2 changes: 1 addition & 1 deletion BossMod/BossModule/AIHints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ public Func<WPos, float> GoalSingleTarget(WPos target, Angle rotation, Positiona
return inPositional ? 2 : 1;
};
}
public Func<WPos, float> GoalSingleTarget(Actor target, Positional positional, float range = 3) => GoalSingleTarget(target.Position, target.Rotation, positional, range + target.HitboxRadius);
public Func<WPos, float> GoalSingleTarget(Actor target, Positional positional, float range = 3) => GoalSingleTarget(target.Position, target.Rotation, positional, range + target.HitboxRadius + 0.5f);

// simple goal zone that returns number of targets in aoes; note that performance is a concern for these functions, and perfection isn't required, so eg they ignore forbidden targets, etc
public Func<WPos, float> GoalAOECircle(float radius)
Expand Down
3 changes: 0 additions & 3 deletions BossMod/BossModule/ZoneModuleConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,4 @@ public sealed class ZoneModuleConfig : ConfigNode

[PropertyDisplay("Use dash abilities for navigation (Smudge, Elusive Jump, etc)")]
public bool UseDash = true;

[PropertyDisplay("Show xan debug UI")]
public bool ShowXanDebugger = false;
}
7 changes: 6 additions & 1 deletion BossMod/Debug/MainDebugWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace BossMod;

class MainDebugWindow(WorldState ws, RotationModuleManager autorot, ActionManagerEx amex, AIHintsBuilder hintBuilder, IDalamudPluginInterface dalamud) : UIWindow("Boss mod debug UI", false, new(300, 200))
class MainDebugWindow(WorldState ws, RotationModuleManager autorot, ZoneModuleManager zmm, ActionManagerEx amex, AIHintsBuilder hintBuilder, IDalamudPluginInterface dalamud) : UIWindow("Boss mod debug UI", false, new(300, 200))
{
private readonly DebugObstacles _debugObstacles = new(hintBuilder.Obstacles, dalamud);
private readonly DebugObjects _debugObjects = new();
Expand Down Expand Up @@ -87,6 +87,11 @@ public override unsafe void Draw()
{
_debugAutorot.Draw();
}
if (ImGui.CollapsingHeader("Solo duty module"))
{
if (zmm.ActiveModule is QuestBattle.QuestBattle qb)
qb.DrawDebugInfo();
}
if (ImGui.CollapsingHeader("Graphics scene"))
{
_debugGraphics.DrawSceneTree();
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Framework/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public unsafe Plugin(IDalamudPluginInterface dalamud, ICommandManager commandMan
_wndReplay = new(_ws, _bossmod, _rotationDB, replayDir);
_wndRotation = new(_rotation, _amex, () => OpenConfigUI("Autorotation Presets"));
_wndAI = new(_ai);
_wndDebug = new(_ws, _rotation, _amex, _hintsBuilder, dalamud);
_wndDebug = new(_ws, _rotation, _zonemod, _amex, _hintsBuilder, dalamud);

dalamud.UiBuilder.DisableAutomaticUiHide = true;
dalamud.UiBuilder.Draw += DrawUI;
Expand Down
4 changes: 2 additions & 2 deletions BossMod/Modules/Dawntrail/TreasureHunt/BullApollyon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public enum OID : uint
{
Boss = 0x4305, // R7.000, x1
Helper = 0x233C, // R0.500, x16, Helper type
// The following 5 need to be killed in order from 1->5 to maximize the rewards
// The following 5 need to be killed in order from 1->5 to maximize the rewards
TuraliOnion = 0x4300, // R0.840, // Icon #1
TuraliEggplant = 0x4301, // R0.840, x0 // Icon #2
TuraliGarlic = 0x4302, // R0.840, x0 // Icon #3
Expand Down Expand Up @@ -101,5 +101,5 @@ public BullApollyonStates(BossModule module) : base(module)
}
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "LegendofIceman", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 993, NameID = 13247)]
[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "LegendofIceman", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 993, NameID = 13247)]
public class BullApollyon(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -372), new ArenaBoundsCircle(20));
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BossMod.Heavensward.DeepDungeon.PalaceoftheDead.D100NybethObdilord;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D100NybethObdilord;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BossMod.Heavensward.DeepDungeon.PalaceoftheDead.D10PalaceDeathgaze;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D10PalaceDeathgaze;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BossMod.Heavensward.DeepDungeon.PalaceoftheDead.D110Alicanto;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D110Alicanto;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BossMod.Modules.Heavensward.DeepDungeon.PalaceOfTheDead.D120Kirtimukha;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D120Kirtimukha;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using BossMod.Heavensward.Dungeon.D06AetherochemicalResearchFacility.D062Harmachis;

namespace BossMod.Modules.Heavensward.DeepDungeon.PalaceOfTheDead.D130Alfard;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D130Alfard;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BossMod.Modules.Heavensward.DeepDungeon.PalaceOfTheDead.D140AhPuch;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D140AhPuch;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BossMod.Modules.Heavensward.DeepDungeon.PalaceOfTheDead.D150Tisiphone;
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D150Tisiphone;

public enum OID : uint
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D160Todesritter;

public enum OID : uint
{
Boss = 0x181D, // R3.920, x1
VoidsentDiscarnate = 0x18EF, // R1.000, x0 (spawn during fight)
Actor1e86e0 = 0x1E86E0, // R2.000, x1, EventObj type
Voidzone = 0x1E858E, // R0.500, x0 (spawn during fight), EventObj type
}

public enum AID : uint
{
AutoAttack = 7179, // Boss->players, no cast, range 8+R(11.92) 90?-degree cone
Geirrothr = 7154, // Boss->self, no cast, range 6+R(9.92) 90?-degree cone
HallOfSorrow = 7155, // Boss->location, no cast, range 9 circle
Infatuation = 7090, // VoidsentDiscarnate->self, 6.5s cast, range 6+R(7) circle
Valfodr = 7156, // Boss->player, 4.0s cast, width 6 rect charge + kb
}

class CleaveAuto(BossModule module) : Components.Cleave(module, ActionID.MakeSpell(AID.AutoAttack), new AOEShapeCone(11.92f, 45.Degrees()), activeWhileCasting: false);
class HallOfSorrow(BossModule module) : Components.PersistentVoidzone(module, 9, m => m.Enemies(OID.Voidzone).Where(z => z.EventState != 7));
class Infatuation(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Infatuation), new AOEShapeCircle(7));
class Valfodr(BossModule module) : Components.BaitAwayChargeCast(module, ActionID.MakeSpell(AID.Valfodr), 3);
class ValfodrKB(BossModule module) : Components.Knockback(module) // note actual knockback is delayed by upto 1.2s in replay
{
private DateTime _activation;

public override IEnumerable<Source> Sources(int slot, Actor actor)
{
if (Module.FindComponent<Valfodr>()?.CurrentBaits.Count > 0)
yield return new(Module.PrimaryActor.Position, 25, _activation, Module.FindComponent<Valfodr>()!.CurrentBaits[0].Shape, Angle.FromDirection(Module.FindComponent<Valfodr>()!.CurrentBaits[0].Target.Position - Module.PrimaryActor.Position), Kind: Kind.DirForward);
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.Valfodr)
{
_activation = Module.CastFinishAt(spell);
StopAtWall = true;
}
}

public override bool DestinationUnsafe(int slot, Actor actor, WPos pos) => (Module.FindComponent<HallOfSorrow>()?.ActiveAOEs(slot, actor).Any(z => z.Shape.Check(pos, z.Origin, z.Rotation)) ?? false) || (Module.FindComponent<Infatuation>()?.ActiveAOEs(slot, actor).Any(z => z.Shape.Check(pos, z.Origin, z.Rotation)) ?? false);
}

class D160TodesritterStates : StateMachineBuilder
{
public D160TodesritterStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<CleaveAuto>()
.ActivateOnEnter<HallOfSorrow>()
.ActivateOnEnter<Infatuation>()
.ActivateOnEnter<Valfodr>()
.ActivateOnEnter<ValfodrKB>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "LegendofIceman", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 214, NameID = 5438)]
public class D160Todesritter(WorldState ws, Actor primary) : BossModule(ws, primary, new(-300, -300), new ArenaBoundsCircle(25));
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D170Yulunggu;

public enum OID : uint
{
Boss = 0x181E, // R5.750, x1
Voidzone = 0x1E9998, // R0.500, x0 (spawn during fight), EventObj type
Actor1E86E0 = 0x1E86E0, // R2.000, x1, EventObj type
}

public enum AID : uint
{
AutoAttack = 6497, // Boss->player, no cast, single-target
Douse = 7158, // Boss->self, 2.0s cast, range 8 circle
Drench = 7160, // Boss->self, no cast, range 10+R ?-degree cone
Electrogenesis = 7161, // Boss->location, 3.0s cast, range 8 circle
FangsEnd = 7159, // Boss->player, no cast, single-target
}

class Douse(BossModule module) : Components.PersistentVoidzoneAtCastTarget(module, 8, ActionID.MakeSpell(AID.Douse), m => m.Enemies(OID.Voidzone).Where(z => z.EventState != 7), 0.8f);

class DouseHaste(BossModule module) : BossComponent(module)
{
private bool _bossInVoidzone;

public override void Update()
{
if (Module.FindComponent<Douse>()?.ActiveAOEs(0, Module.PrimaryActor).Any(z => z.Shape.Check(Module.PrimaryActor.Position, z.Origin, z.Rotation)) ?? false)
_bossInVoidzone = true;
else
_bossInVoidzone = false;
}

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (_bossInVoidzone && Module.PrimaryActor.TargetID == actor.InstanceID)
hints.Add("Pull the boss out of the water puddle!");
if (_bossInVoidzone && Module.PrimaryActor.TargetID != actor.InstanceID && actor.Role == Role.Tank)
hints.Add("Consider provoking and pulling the boss out of the water puddle.");
}
}

class Drench(BossModule module) : Components.Cleave(module, ActionID.MakeSpell(AID.Drench), new AOEShapeCone(15.75f, 45.Degrees()), activeWhileCasting: false);

class Electrogenesis(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Electrogenesis), 8, "Get out of the AOE");

class D170YulungguStates : StateMachineBuilder
{
public D170YulungguStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<Douse>()
.ActivateOnEnter<DouseHaste>()
.ActivateOnEnter<Drench>()
.ActivateOnEnter<Electrogenesis>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "LegendofIceman", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 215, NameID = 5449)]
public class D170Yulunggu(WorldState ws, Actor primary) : BossModule(ws, primary, new(-300, -300), new ArenaBoundsCircle(25));
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
namespace BossMod.Heavensward.DeepDungeon.PalaceOfTheDead.D180Dendainsonne;

public enum OID : uint
{
Boss = 0x181F, // R11.600, x1
TornadoVoidZones = 0x18F0, // R1.000, x0 (spawn during fight)
Actor1E86E0 = 0x1E86E0, // R2.000, x1, EventObj type
}

public enum AID : uint
{
AutoAttack = 6497, // Boss->player, no cast, single-target
CharybdisCast = 7163, // Boss->location, 3.0s cast, range 6 circle
CharybdisTornado = 7164, // TornadoVoidZones->self, no cast, range 6 circle
EclipticMeteor = 7166, // Boss->self, 6.0s cast, range 50 circle
Maelstrom = 7167, // TornadoVoidZones->self, 1.3s cast, range 10 circle
Thunderbolt = 7162, // Boss->self, 2.5s cast, range 5+R 120-degree cone
Trounce = 7165, // Boss->self, 2.5s cast, range 40+R 60-degree cone
}

class Charybdis(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.CharybdisCast), 6);
class Maelstrom(BossModule module) : Components.PersistentVoidzone(module, 10, m => m.Enemies(OID.TornadoVoidZones));
class Trounce(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Trounce), new AOEShapeCone(51.6f, 30.Degrees()));
class EclipticMeteor(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.EclipticMeteor), "Kill him before he kills you! 80% max HP damage incoming!");
class Thunderbolt(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Thunderbolt), new AOEShapeCone(16.6f, 60.Degrees()));

class EncounterHints(BossModule module) : BossComponent(module)
{
private int NumCast { get; set; }

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.CharybdisCast or AID.Trounce or AID.Thunderbolt)
++NumCast;

if ((AID)spell.Action.ID is AID.EclipticMeteor)
NumCast = 11;

if (NumCast == 10)
{
// reset the rotation back to the beginning. This loops continuously until you hit the hp threshold (15.0% here)
NumCast = 0;
}
}

public override void AddGlobalHints(GlobalHints hints)
{
switch (NumCast)
{
case 0:
hints.Add("Thunderbolt -> Charybdis x2 -> Thunderbolt -> Trounce from South Wall.");
break;
case 1:
hints.Add("Charybdis x2 -> Thunderbolt -> Trounce from South Wall.");
break;
case 2:
hints.Add("Charybdis x1 -> Thunderbolt -> Trounce from South Wall!");
break;
case 3:
hints.Add("Thunderbolt -> Trounce from South Wall!");
break;
case 4:
hints.Add("Boss is running to the South wall to cast Trounce!");
break;
case 5:
hints.Add("Charybdis x2 -> Thunderbolt -> Charybdis -> Trounce from North Wall.");
break;
case 6:
hints.Add("Charybdis x1 -> Thunderbolt -> Charybdis -> Trounce from North Wall.");
break;
case 7:
hints.Add("Thunderbolt -> Charybdis -> Trounce from North Wall!");
break;
case 8:
hints.Add("Charybdis -> Trounce from North Wall!");
break;
case 9:
hints.Add("Boss is running to the North wall to cast Trounce!");
break;
}
}
}

class Hints(BossModule module) : BossComponent(module)
{
public override void AddGlobalHints(GlobalHints hints)
{
hints.Add($"{Module.PrimaryActor.Name} will cast Trounce (Cone AOE) from the South and North wall. \nMake sure to stay near him to dodge the AOE. \n{Module.PrimaryActor.Name} will also cast Ecliptic Meteor at 15% HP, plan accordingly!");
}
}

class D180DendainsonneStates : StateMachineBuilder
{
public D180DendainsonneStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<Thunderbolt>()
.ActivateOnEnter<Charybdis>()
.ActivateOnEnter<Maelstrom>()
.ActivateOnEnter<Trounce>()
.ActivateOnEnter<EclipticMeteor>()
.ActivateOnEnter<EncounterHints>()
.DeactivateOnEnter<Hints>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "LegendofIceman", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 216, NameID = 5461)]
public class D180Dendainsonne : BossModule
{
public D180Dendainsonne(WorldState ws, Actor primary) : base(ws, primary, new(-300, -300), new ArenaBoundsCircle(25))
{
ActivateComponent<Hints>();
}
}
Loading

0 comments on commit 7b3194f

Please sign in to comment.