Skip to content

Commit

Permalink
Merge pull request #460 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
Fafnir module updated
  • Loading branch information
CarnifexOptimus authored Nov 24, 2024
2 parents ff1b0fc + 8402cf3 commit d61eb3a
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 120 deletions.
17 changes: 11 additions & 6 deletions BossMod/AI/AIBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,16 @@ private void AdjustTargetPositional(Actor player, ref Targeting targeting)

private NavigationDecision BuildNavigationDecision(Actor player, Actor master, ref Targeting targeting)
{
var target = autorot.WorldState.Actors.Find(player.TargetID);
if (_config.ForbidMovement)
return new() { LeewaySeconds = float.MaxValue };
if (_followMaster && !_config.FollowTarget || _followMaster && _config.FollowTarget && target == null)
return NavigationDecision.Build(_naviCtx, WorldState, autorot.Hints, player, master.Position, _config.MaxDistanceToSlot, new(), Positional.Any);
if (_followMaster && _config.FollowTarget && target != null)
return NavigationDecision.Build(_naviCtx, WorldState, autorot.Hints, player, target.Position, target.HitboxRadius + (_config.DesiredPositional != Positional.Any ? 2.6f : _config.MaxDistanceToTarget), target.Rotation, target != player ? _config.DesiredPositional : Positional.Any);
if (AIPreset == null || _config.OverrideAutorotation)
{
var target = autorot.WorldState.Actors.Find(player.TargetID);
if (_followMaster && !_config.FollowTarget || _followMaster && _config.FollowTarget && target == null)
return NavigationDecision.Build(_naviCtx, WorldState, autorot.Hints, player, master.Position, _config.MaxDistanceToSlot, new(), Positional.Any);
if (_followMaster && _config.FollowTarget && target != null)
return NavigationDecision.Build(_naviCtx, WorldState, autorot.Hints, player, target.Position, target.HitboxRadius + (_config.DesiredPositional != Positional.Any ? 2.6f : _config.MaxDistanceToTarget), target.Rotation, target != player ? _config.DesiredPositional : Positional.Any);
}
if (targeting.Target == null)
return NavigationDecision.Build(_naviCtx, autorot.WorldState, autorot.Hints, player, null, 0, new(), Positional.Any);
var adjRange = targeting.PreferredRange + player.HitboxRadius + targeting.Target.Actor.HitboxRadius;
Expand All @@ -147,7 +150,7 @@ private NavigationDecision BuildNavigationDecision(Actor player, Actor master, r
}
}
var adjRotation = targeting.PreferTanking ? targeting.Target.DesiredRotation : targeting.Target.Actor.Rotation;
return NavigationDecision.Build(_naviCtx, WorldState, autorot.Hints, player, targeting.Target.Actor.Position, adjRange, adjRotation, targeting.PreferredPosition);
return NavigationDecision.Build(_naviCtx, WorldState, autorot.Hints, player, autorot.Hints.RecommendedPositional.Target?.Position, adjRange, adjRotation, autorot.Hints.RecommendedPositional.Pos);
}

private void FocusMaster(Actor master)
Expand Down Expand Up @@ -224,6 +227,8 @@ public void DrawDebug()
configModified |= ImGui.Checkbox("Forbid movement", ref _config.ForbidMovement);
ImGui.SameLine();
configModified |= ImGui.Checkbox("Follow during combat", ref _config.FollowDuringCombat);
ImGui.SameLine();
configModified |= ImGui.Checkbox("Override autorotation values", ref _config.OverrideAutorotation);
ImGui.Spacing();
configModified |= ImGui.Checkbox("Follow during active boss module", ref _config.FollowDuringActiveBossModule);
ImGui.SameLine();
Expand Down
3 changes: 3 additions & 0 deletions BossMod/AI/AIConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ sealed class AIConfig : ConfigNode
[PropertyDisplay("Max distance to target")]
public float MaxDistanceToTarget = 2.6f;

[PropertyDisplay("Override autorotation values", tooltip: "Uses your custom positional and distance settings instead of autorotation specific values")]
public bool OverrideAutorotation = true;

[PropertyDisplay("Enable auto AFK", tooltip: "Enables auto AFK if out of combat. While AFK AI will not use autorotation or target anything")]
public bool AutoAFK = false;

Expand Down
9 changes: 9 additions & 0 deletions BossMod/AI/AIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ private void OnCommand(string cmd, string message)
case "FOLLOWTARGET":
configModified = ToggleFollowTarget(messageData);
break;
case "OVERRIDEAUTOROTATION":
configModified = ToggleAutorotationOverride();
break;
case "POSITIONAL":
configModified = HandlePositionalCommand(messageData);
break;
Expand Down Expand Up @@ -225,6 +228,12 @@ private bool ToggleFocusTargetLeader()
return true;
}

private bool ToggleAutorotationOverride()
{
_config.OverrideAutorotation = !_config.OverrideAutorotation;
return true;
}

private bool HandleFollowCommand(string[] messageData)
{
if (messageData.Length < 2)
Expand Down
37 changes: 23 additions & 14 deletions BossMod/Components/StackSpread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,29 +306,32 @@ public class StackWithIcon(BossModule module, uint icon, ActionID aid, float rad

// generic single hit "line stack" component, usually do not have an iconID, instead players get marked by cast event
// usually these have 50 range and 4 halfWidth, but it can be modified
public class LineStack(BossModule module, ActionID? aidMarker, ActionID aidResolve, float activationDelay = 5.1f, float range = 50, float halfWidth = 4, int minStackSize = 4, int maxStackSize = int.MaxValue, int maxCasts = 1, bool markerIsFinalTarget = true) : GenericBaitAway(module)
public class LineStack(BossModule module, ActionID? aidMarker, ActionID aidResolve, float activationDelay = 5.1f, float range = 50, float halfWidth = 4, int minStackSize = 4, int maxStackSize = int.MaxValue, int maxCasts = 1, bool markerIsFinalTarget = true, uint? iconid = null) : GenericBaitAway(module)
{
public LineStack(BossModule module, uint iconid, ActionID aidResolve, float activationDelay = 5.1f, float range = 50, float halfWidth = 4, int minStackSize = 4, int maxStackSize = int.MaxValue, int maxCasts = 1, bool markerIsFinalTarget = true) : this(module, null, aidResolve, activationDelay, range, halfWidth, minStackSize, maxStackSize, maxCasts, markerIsFinalTarget, iconid) { }

// TODO: add forbidden slots logic?
// TODO: add logic for min and max stack size
public ActionID? AidMarker { get; init; } = aidMarker;
public ActionID AidResolve { get; init; } = aidResolve;
public float ActionDelay { get; init; } = activationDelay;
public float Range { get; init; } = range;
public float HalfWidth { get; init; } = halfWidth;
public int MaxStackSize { get; init; } = maxStackSize;
public int MinStackSize { get; init; } = minStackSize;
public int MaxCasts { get; init; } = maxCasts; // for stacks where the final AID hits multiple times
public bool MarkerIsFinalTarget { get; init; } = markerIsFinalTarget; // rarely the marked player is not the target of the line stack
public HashSet<Actor> ForbiddenActors { get; init; } = [];
public ActionID? AidMarker = aidMarker;
public ActionID AidResolve = aidResolve;
public float ActionDelay = activationDelay;
public float Range = range;
public float HalfWidth = halfWidth;
public int MaxStackSize = maxStackSize;
public int MinStackSize = minStackSize;
public int MaxCasts = maxCasts; // for stacks where the final AID hits multiple times
public bool MarkerIsFinalTarget = markerIsFinalTarget; // rarely the marked player is not the target of the line stack
public HashSet<Actor> ForbiddenActors = [];
private int castCounter;
public uint? Iconid = iconid;
public const string HintStack = "Stack!";
public const string HintAvoidOther = "GTFO from other line stacks!";
public const string HintAvoid = "GTFO from line stacks!";
private readonly AOEShape rect = new AOEShapeRect(range, halfWidth);

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if (AidMarker == null)
if (AidMarker == null && Iconid == null)
return;
if (spell.Action == AidMarker)
CurrentBaits.Add(new(caster, WorldState.Actors.Find(spell.MainTargetID)!, rect, WorldState.FutureTime(ActionDelay)));
Expand Down Expand Up @@ -357,17 +360,23 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)
}
}

public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (Iconid != null && iconID == Iconid && WorldState.Actors.Find(targetID) is var target && target != null)
CurrentBaits.Add(new(actor, WorldState.Actors.Find(targetID)!, rect, WorldState.FutureTime(ActionDelay)));
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if (AidMarker != null)
if (AidMarker != null || Iconid != null)
return;
if (spell.Action == AidResolve)
CurrentBaits.Add(new(caster, WorldState.Actors.Find(spell.TargetID)!, rect, Module.CastFinishAt(spell)));
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (AidMarker != null)
if (AidMarker != null || Iconid != null)
return;
if (spell.Action == AidResolve && CurrentBaits.Count != 0)
{
Expand Down
10 changes: 5 additions & 5 deletions BossMod/Config/ConfigRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ public void SaveToFile(FileInfo file)
}
}

public List<string> ConsoleCommand(IReadOnlyList<string> args, bool save = true)
public List<string> ConsoleCommand(ReadOnlySpan<string> args, bool save = true)
{
List<string> result = [];
if (args.Count == 0)
if (args.Length == 0)
{
result.Add("Usage: /vbm cfg <config-type> <field> <value>");
result.Add("Both config-type and field can be shortened. Valid config-types:");
Expand All @@ -105,7 +105,7 @@ public List<string> ConsoleCommand(IReadOnlyList<string> args, bool save = true)
foreach (var n in matchingNodes)
result.Add($"- {n.GetType().Name}");
}
else if (args.Count == 1)
else if (args.Length == 1)
{
result.Add("Usage: /vbm cfg <config-type> <field> <value>");
result.Add($"Valid fields for {matchingNodes[0].GetType().Name}:");
Expand Down Expand Up @@ -139,7 +139,7 @@ public List<string> ConsoleCommand(IReadOnlyList<string> args, bool save = true)
{
try
{
if (args.Count == 2)
if (args.Length == 2)
result.Add(matchingFields[0].GetValue(matchingNodes[0])?.ToString() ?? $"Failed to get value of '{args[2]}'");
else
{
Expand All @@ -158,7 +158,7 @@ public List<string> ConsoleCommand(IReadOnlyList<string> args, bool save = true)
}
catch (Exception e)
{
if (args.Count == 2)
if (args.Length == 2)
result.Add($"Failed to get value of {matchingNodes[0].GetType().Name}.{matchingFields[0].Name} : {e}");
else
result.Add($"Failed to set {matchingNodes[0].GetType().Name}.{matchingFields[0].Name} to {args[2]}: {e}");
Expand Down
1 change: 1 addition & 0 deletions BossMod/Config/ConfigUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public void Draw()
{ "positional X", "Switch to positional when following targets. (any, rear, flank, front)" },
{ "maxdistancetarget X", "Sets max distance to target. (default = 2.6)" },
{ "maxdistanceslot X", "Sets max distance to slot. (default = 1)" },
{ "overrideautorotation", "Overrides autorotation distance and positional values with custom settings." },
{ "setpresetname X", "Sets an autorotation preset for the AI, eg. setpresetname vbm default." }
};

Expand Down
19 changes: 11 additions & 8 deletions BossMod/Framework/IPCProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ sealed class IPCProvider : IDisposable
public IPCProvider(RotationModuleManager autorotation, ActionManagerEx amex, MovementOverride movement, AIManager ai)
{
Register("HasModuleByDataId", (uint dataId) => BossModuleRegistry.FindByOID(dataId) != null);
Register("Configuration", (IReadOnlyList<string> args, bool save) => Service.Config.ConsoleCommand(args, save));

Register("Configuration", (List<string> args, bool save) => Service.Config.ConsoleCommand(args.AsSpan(), save));
Register("Presets.Get", (string name) =>
{
var preset = autorotation.Database.Presets.FindPresetByName(name);
Expand Down Expand Up @@ -80,6 +79,10 @@ public IPCProvider(RotationModuleManager autorotation, ActionManagerEx amex, Mov
ms.Settings.Add(new(default, iTrack, new() { Option = iOpt }));
return true;
});

Register("AI.SetPreset", (string name) =>
ai.SetAIPreset(autorotation.Database.Presets.VisiblePresets.FirstOrDefault(x => x.Name == name)));
Register("AI.GetPreset", () => ai.GetAIPreset);
}

public void Dispose() => _disposeActions?.Invoke();
Expand Down Expand Up @@ -119,10 +122,10 @@ private void Register<T1, T2, T3, T4, TRet>(string name, Func<T1, T2, T3, T4, TR
// _disposeActions += p.UnregisterAction;
//}

//private void Register<T1>(string name, Action<T1> func)
//{
// var p = Service.PluginInterface.GetIpcProvider<T1, object>("BossMod." + name);
// p.RegisterAction(func);
// _disposeActions += p.UnregisterAction;
//}
private void Register<T1>(string name, Action<T1> func)
{
var p = Service.PluginInterface.GetIpcProvider<T1, object>("BossMod." + name);
p.RegisterAction(func);
_disposeActions += p.UnregisterAction;
}
}
71 changes: 18 additions & 53 deletions BossMod/Modules/Dawntrail/Alliance/A12Fafnir/A12Fafnir.cs
Original file line number Diff line number Diff line change
@@ -1,69 +1,34 @@
namespace BossMod.Dawntrail.Alliance.A12Fafnir;

class DarkMatterBlast(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.DarkMatterBlast));
class HorridRoar2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.HorridRoarAOE), 4);
class HorridRoar3(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.HorridRoarSpread), 8);
class HurricaneWingRaidwide(BossModule module) : Components.RaidwideCastDelay(module, ActionID.MakeSpell(AID.HurricaneWingVisual), ActionID.MakeSpell(AID.HurricaneWing1), 2.7f, "Raidwide x9");
class HorridRoarAOE(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.HorridRoarAOE), 4);
class HorridRoarSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.HorridRoarSpread), 8);
class SpikeFlail(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SpikeFlail), new AOEShapeCone(80, 135.Degrees()));
class Venom(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Venom), new AOEShapeCone(30, 60.Degrees()));
class PestilentSphere(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.PestilentSphere));
class WingedTerror(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WingedTerror), new AOEShapeRect(70, 12.5f));
class AbsoluteTerror(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.AbsoluteTerror), new AOEShapeRect(70, 10));
class Touchdown(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Touchdown), new AOEShapeCircle(24));

class HurricaneWing1(BossModule module) : Components.ConcentricAOEs(module, _shapes)
class BalefulBreath(BossModule module) : Components.LineStack(module, (uint)IconID.LineStack, ActionID.MakeSpell(AID.BalefulBreath2), 8.2f, 70, 3, PartyState.MaxAllianceSize, PartyState.MaxAllianceSize, 3, false);
class SharpSpike(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCircle(4), (uint)IconID.Tankbuster, ActionID.MakeSpell(AID.SharpSpike), 6.2f, true)
{
private static readonly AOEShape[] _shapes = [new AOEShapeCircle(9), new AOEShapeDonut(9, 16), new AOEShapeDonut(16, 23), new AOEShapeDonut(23, 30)];

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.HurricaneWingConcentricA1)
AddSequence(Arena.Center, Module.CastFinishAt(spell));
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
public override void AddGlobalHints(GlobalHints hints)
{
if (Sequences.Count > 0)
{
var order = (AID)spell.Action.ID switch
{
AID.HurricaneWingConcentricA1 => 0,
AID.HurricaneWingConcentricA2 => 1,
AID.HurricaneWingConcentricA3 => 2,
AID.HurricaneWingConcentricA4 => 3,
_ => -1
};
AdvanceSequence(order, Arena.Center, WorldState.FutureTime(3));
}
if (CurrentBaits.Count > 0)
hints.Add("Tankbuster cleave");
}
}

class HurricaneWing2(BossModule module) : Components.ConcentricAOEs(module, _shapes)
{
private static readonly AOEShape[] _shapes = [new AOEShapeCircle(9), new AOEShapeDonut(9, 16), new AOEShapeDonut(16, 23), new AOEShapeDonut(23, 30)];

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.HurricaneWingConcentricB1)
AddSequence(Arena.Center, Module.CastFinishAt(spell));
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if (Sequences.Count > 0)
{
var order = (AID)spell.Action.ID switch
{
AID.HurricaneWingConcentricB1 => 0,
AID.HurricaneWingConcentricB2 => 1,
AID.HurricaneWingConcentricB3 => 2,
AID.HurricaneWingConcentricB4 => 3,
_ => -1
};
AdvanceSequence(order, Arena.Center, WorldState.FutureTime(3));
}
}
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "The Combat Reborn Team (LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13662)]
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13662)]
public class A12Fafnir(WorldState ws, Actor primary) : BossModule(ws, primary, ArenaCenter, new ArenaBoundsCircle(34.5f))
{
public static readonly WPos ArenaCenter = new(-500, 600);
public static readonly ArenaBoundsCircle DefaultBounds = new(30);
public static readonly ArenaBoundsCircle FireArena = new(16);

protected override void DrawEnemies(int pcSlot, Actor pc)
{
Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly));
}
}
Loading

0 comments on commit d61eb3a

Please sign in to comment.