Skip to content

Commit

Permalink
Merge pull request #433 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
Xelphatol modules, AI afk mode setting
  • Loading branch information
CarnifexOptimus authored Nov 10, 2024
2 parents cc03d24 + c4a7516 commit bc7421a
Show file tree
Hide file tree
Showing 19 changed files with 471 additions and 26 deletions.
2 changes: 1 addition & 1 deletion BossMod/AI/AIBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Execute(Actor player, Actor master)
if (_config.FocusTargetLeader)
FocusMaster(master);

_afkMode = !master.InCombat && (WorldState.CurrentTime - _masterLastMoved).TotalSeconds > 10;
_afkMode = _config.AutoAFK && !master.InCombat && (WorldState.CurrentTime - _masterLastMoved).TotalSeconds > _config.AFKModeTimer;
var gazeImminent = autorot.Hints.ForbiddenDirections.Count > 0 && autorot.Hints.ForbiddenDirections[0].activation <= WorldState.FutureTime(0.5f);
var pyreticImminent = autorot.Hints.ImminentSpecialMode.mode == AIHints.SpecialMode.Pyretic && autorot.Hints.ImminentSpecialMode.activation <= WorldState.FutureTime(1);
var forbidActions = _config.ForbidActions || _afkMode || gazeImminent || pyreticImminent;
Expand Down
6 changes: 6 additions & 0 deletions BossMod/AI/AIConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,11 @@ sealed class AIConfig : ConfigNode
[PropertyDisplay("Max distance to target")]
public float MaxDistanceToTarget = 2.6f;

[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;

[PropertyDisplay("Enable out of combat AFK mode", tooltip: "Time in seconds out of combat until AFK mode enables. Any movement will reset timer or disable AFK mode if already active.")]
public float AFKModeTimer = 10;

public string? AIAutorotPresetName;
}
20 changes: 20 additions & 0 deletions BossMod/BossModule/Shapes.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Clipper2Lib;

namespace BossMod;

public abstract record class Shape
Expand All @@ -23,6 +25,24 @@ public record class PolygonCustom(IEnumerable<WPos> Vertices) : Shape
public override string ToString() => $"{nameof(PolygonCustom)}:{string.Join(",", Vertices.Select(v => $"{v.X},{v.Z}"))}";
}

// for custom polygons defined by an IEnumerable of vertices with an offset, eg to account for hitbox radius
public record class PolygonCustomO(IEnumerable<WPos> Vertices, float Offset) : Shape
{
public override List<WDir> Contour(WPos center)
{
var originalPath = new Path64(Vertices.Select(v => new Point64((long)(v.X * PolygonClipper.Scale), (long)(v.Z * PolygonClipper.Scale))));
ClipperOffset co = new();
co.AddPath(originalPath, JoinType.Miter, EndType.Polygon);
Paths64 solution = [];
co.Execute(Offset * PolygonClipper.Scale, solution);
var offsetPath = solution[0];
var offsetContour = offsetPath.Select(p => new WDir((float)(p.X * PolygonClipper.InvScale - center.X), (float)(p.Y * PolygonClipper.InvScale - center.Z))).ToList();
return offsetContour;
}

public override string ToString() => $"{nameof(PolygonCustomO)}:{string.Join(",", Vertices.Select(v => $"{v.X},{v.Z}"))},{Offset}";
}

public record class Donut(WPos Center, float InnerRadius, float OuterRadius) : Shape
{
public override List<WDir> Contour(WPos center) => CurveApprox.Donut(InnerRadius, OuterRadius, MaxApproxError).Select(p => p + (Center - center)).ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public enum AID : uint
SurgeNPCs = 39736, // Helper->self, 8.5s cast, range 40 width 40 rect, knockback 15 dir left/right, only seems to apply to NPCs
Surge = 36367, // Boss->location, 8.0s cast, range 40 width 40 rect, knockback 30 dir left/right

Electray = 38320, // Helper->player, 8.0s cast, range 5 circle
Electray = 38320 // Helper->player, 8.0s cast, range 5 circle
}

class ArenaChanges(BossModule module) : Components.GenericAOEs(module)
Expand Down
6 changes: 4 additions & 2 deletions BossMod/Modules/Dawntrail/Hunt/RankS/ArchAethereater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum OID : uint
public enum AID : uint
{
AutoAttack = 39517, // Boss->player, no cast, single-target

Aethermodynamics1 = 39840, // Boss->self, 5.0s cast, range 40 circle
Aethermodynamics2 = 39512, // Boss->self, 5.0s cast, range 40 circle
Aethermodynamics3 = 39511, // Boss->self, 5.0s cast, range 40 circle
Expand Down Expand Up @@ -131,9 +132,10 @@ class SoullessStreamFireBlizzardCombo(BossModule module) : Components.GenericAOE

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
yield return _aoes[0] with { Color = Colors.Danger };
if (_aoes.Count > 1)
if (count > 1)
yield return _aoes[1];
}

Expand Down
13 changes: 8 additions & 5 deletions BossMod/Modules/Dawntrail/Hunt/RankS/AtticusThePrimogenitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum OID : uint
public enum AID : uint
{
AutoAttack = 39015, // Boss->player, no cast, single-target

HeadSpeaks = 39883, // Boss->self, no cast, single-target
HeadSpeaks2 = 39884, // Boss->self, no cast, single-target
BreathSequenceFirstFront = 39003, // Boss->self, 5.0s cast, range 60 120-degree cone
Expand Down Expand Up @@ -84,9 +85,10 @@ class BreathSequence(BossModule module) : Components.GenericAOEs(module)

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
yield return _aoes[0] with { Color = Colors.Danger };
if (_aoes.Count > 1)
if (count > 1)
yield return _aoes[1];
}

Expand All @@ -103,14 +105,15 @@ public override void OnActorNpcYell(Actor actor, ushort id)

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
switch ((AID)spell.Action.ID)
{
case AID.BreathSequenceFirstFront:
case AID.BreathSequenceFirstLeft:
case AID.BreathSequenceFirstRight:
for (var i = 0; i < _aoes.Count; ++i)
_aoes[i] = new(_aoes[i].Shape, _aoes[i].Origin, _aoes[i].Rotation, Module.CastFinishAt(spell, 2.3f * i));
for (var i = 0; i < count; ++i)
_aoes[i] = _aoes[i] with { Activation = Module.CastFinishAt(spell, 2.3f * i) };
break;
}
}
Expand Down
5 changes: 3 additions & 2 deletions BossMod/Modules/Dawntrail/Hunt/RankS/Ihnuxokiy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ private enum Aetherspark { None, Thunderspark, CyclonicRing }

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
yield return _aoes[0] with { Color = Colors.Danger };
if (_aoes.Count > 1)
if (count > 1)
yield return _aoes[1];
}

Expand Down
5 changes: 3 additions & 2 deletions BossMod/Modules/Dawntrail/Hunt/RankS/Neyoozoteel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ class SapSpiller(BossModule module) : Components.GenericAOEs(module)

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
{
yield return _aoes[0] with { Color = Colors.Danger };
foreach (var a in _aoes.Skip(1).Take(_aoes.Count - 1))
foreach (var a in _aoes.Skip(1).Take(count - 1))
yield return a;
}
}
Expand Down
8 changes: 5 additions & 3 deletions BossMod/Modules/Dawntrail/Hunt/RankS/Sansheya.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum OID : uint
public enum AID : uint
{
AutoAttack = 870, // Boss/4379->player, no cast, single-target

CullingBlade = 39295, // Boss->self, 4.0s cast, range 80 circle
PyreOfRebirth = 39288, // Boss->self, 4.0s cast, range 32 circle, status effect boiling, turns into pyretic
FiresDomain = 39285, // Boss->players, 5.0s cast, width 6 rect charge
Expand Down Expand Up @@ -77,10 +78,11 @@ class TwinscorchedHaloVeil(BossModule module) : Components.GenericAOEs(module)

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
yield return _aoes[0] with { Color = Colors.Danger };
if (_aoes.Count > 1)
yield return _aoes[1] with { Risky = _aoes.Count == 2 };
if (count > 1)
yield return _aoes[1] with { Risky = _aoes[1].Shape != cone };
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
Expand Down
6 changes: 4 additions & 2 deletions BossMod/Modules/Dawntrail/Hunt/RankS/TheForecaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum OID : uint
public enum AID : uint
{
AutoAttack = 872, // Boss->player, no cast, single-target

GaleForceWinds = 38534, // Boss->self, 4.0s cast, range 40 width 40 rect
BlizzardConditions = 38535, // Boss->self, 4.0s cast, range 40 width 5 cross
Hyperelectricity = 38533, // Boss->self, 4.0s cast, range 10 circle
Expand Down Expand Up @@ -58,9 +59,10 @@ private enum ClimateChange { None, G2B, B2W, H2G, W2H }

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
var count = _aoes.Count;
if (count > 0)
yield return _aoes[0] with { Color = Colors.Danger };
if (_aoes.Count > 1)
if (count > 1)
yield return _aoes[1] with { Risky = false };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@ protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRoles
var e = hints.PotentialTargets[i];
e.Priority = (OID)e.Actor.OID switch
{
OID.PoroggoChoirtoad => 2,
OID.Boss => 1,
OID.PoroggoChoirtoad => 1,
_ => 0
};
}
Expand Down
13 changes: 13 additions & 0 deletions BossMod/Modules/Heavensward/Dungeon/D11Antitower/D113Calcabrina.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,17 @@ protected override void DrawEnemies(int pcSlot, Actor pc)
Arena.Actors(Enemies(OID.Brina).Concat(Enemies(OID.Boss)).Concat(Enemies(OID.Calcabrina)));
Arena.Actors(Enemies(OID.CalcaPlayer1).Concat(Enemies(OID.CalcaPlayer2)).Concat(Enemies(OID.BrinaPlayer1)).Concat(Enemies(OID.BrinaPlayer2)), Colors.Vulnerable);
}

protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
for (var i = 0; i < hints.PotentialTargets.Count; ++i)
{
var e = hints.PotentialTargets[i];
e.Priority = (OID)e.Actor.OID switch
{
OID.BrinaPlayer1 or OID.BrinaPlayer2 or OID.CalcaPlayer1 or OID.CalcaPlayer2 => 1,
_ => 0
};
}
}
}
117 changes: 117 additions & 0 deletions BossMod/Modules/Heavensward/Dungeon/D15Xelphatol/D151NuzalHueloc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
namespace BossMod.Heavensward.Dungeon.D15Xelphatol.D151NuzalHueloc;

public enum OID : uint
{
Boss = 0x179B, // R1.5
FloatingTurret = 0x179E, // R1.0
IxaliStitcher = 0x179C, // R1.08
Airstone = 0x179D // R1.5
}

public enum AID : uint
{
AutoAttack1 = 872, // Boss->player, no cast, single-target
AutoAttack2 = 6605, // FloatingTurret->player, no cast, single-target
AutoAttack3 = 870, // IxaliStitcher->player, no cast, single-target
ShortBurst1 = 6598, // Boss->player, no cast, single-target
ShortBurst2 = 6603, // FloatingTurret->player, 3.0s cast, single-target

WindBlast = 6599, // Boss->self, 3.0s cast, range 60+R width 8 rect
Lift = 6601, // Boss->self, 3.0s cast, single-target
AirRaid = 6602, // Boss->location, no cast, range 50 circle
HotBlast = 6604, // FloatingTurret->self, 6.0s cast, range 25 circle
LongBurst = 6600 // Boss->player, 3.0s cast, single-target
}

public enum SID : uint
{
Invincibility = 775 // none->Boss/FloatingTurret, extra=0x0
}

class Airstone(BossModule module) : BossComponent(module)
{
public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (Module.Enemies(OID.Airstone).Any(x => !x.IsDead))
hints.Add("Destroy the airstones to remove invincibility!");
}
}

class WindBlast(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WindBlast), new AOEShapeRect(61.5f, 4));

class HotBlast(BossModule module) : Components.GenericAOEs(module)
{
private static readonly AOEShapeCircle circle = new(4, true);
private AOEInstance? _aoe;
private const string RiskHint = "Go under boss!";

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

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.HotBlast)
_aoe = new(circle, Module.PrimaryActor.Position, default, Module.CastFinishAt(spell), Colors.SafeFromAOE);
}

public override void Update()
{
if (_aoe != null && (WorldState.CurrentTime - _aoe.Value.Activation).TotalSeconds >= 1)
_aoe = null;
}

public override void AddHints(int slot, Actor actor, TextHints hints)
{
var activeAOEs = ActiveAOEs(slot, actor).ToList();
if (activeAOEs.Any(c => !c.Check(actor.Position)))
hints.Add(RiskHint);
else if (activeAOEs.Any(c => c.Check(actor.Position)))
hints.Add(RiskHint, false);
}
}

class D151NuzalHuelocStates : StateMachineBuilder
{
public D151NuzalHuelocStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<Airstone>()
.ActivateOnEnter<WindBlast>()
.ActivateOnEnter<HotBlast>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 182, NameID = 5265)]
public class D151NuzalHueloc(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena)
{
private static readonly WPos[] vertices = [new(-73.36f, -91.53f), new(-67.17f, -90.22f), new(-66.55f, -89.97f), new(-64.94f, -89.05f), new(-64.45f, -88.60f),
new(-53.36f, -75.39f), new(-53.26f, -74.73f), new(-52.97f, -69.07f), new(-54.26f, -62.76f), new(-54.54f, -62.01f),
new(-57.75f, -56.44f), new(-62.76f, -51.91f), new(-68.83f, -49.18f), new(-75.29f, -48.72f), new(-75.94f, -48.76f),
new(-79.79f, -49.43f), new(-90.22f, -55.45f), new(-92.52f, -57.81f), new(-95.38f, -64.42f), new(-96.08f, -70.82f),
new(-96.01f, -71.49f), new(-94.72f, -77.66f), new(-91.42f, -83.42f), new(-86.42f, -88.01f), new(-80.32f, -90.77f),
new(-73.80f, -91.52f)];
private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]);

protected override void DrawEnemies(int pcSlot, Actor pc)
{
Arena.Actors(Enemies(OID.IxaliStitcher).Concat([PrimaryActor]).Concat(Enemies(OID.FloatingTurret)).Concat(Enemies(OID.Airstone)));
}

protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
for (var i = 0; i < hints.PotentialTargets.Count; ++i)
{
var e = hints.PotentialTargets[i];
if (e.Actor.FindStatus(SID.Invincibility) != null)
{
e.Priority = -2;
continue;
}
e.Priority = (OID)e.Actor.OID switch
{
OID.Airstone => 2,
OID.FloatingTurret => 1,
_ => 0
};
}
}
}
Loading

0 comments on commit bc7421a

Please sign in to comment.