Skip to content

Commit

Permalink
Merge pull request #621 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
BA Ozma module
  • Loading branch information
CarnifexOptimus authored Feb 22, 2025
2 parents a433930 + eaa637f commit c936477
Show file tree
Hide file tree
Showing 17 changed files with 830 additions and 59 deletions.
64 changes: 53 additions & 11 deletions BossMod/Components/BaitAway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,38 +75,80 @@ public List<Bait> ActiveBaitsNotOn(Actor target)

public WPos BaitOrigin(Bait bait) => (CenterAtTarget ? bait.Target : bait.Source).Position;
public bool IsClippedBy(Actor actor, Bait bait) => bait.Shape.Check(actor.Position, BaitOrigin(bait), bait.Rotation);
public IEnumerable<Actor> PlayersClippedBy(Bait bait) => Raid.WithoutSlot().Exclude(bait.Target).InShape(bait.Shape, BaitOrigin(bait), bait.Rotation);
public List<Actor> PlayersClippedBy(Bait bait)
{
var actors = Raid.WithoutSlot();
var len = actors.Length;
List<Actor> result = new(len);
for (var i = 0; i < len; ++i)
{
var actor = actors[i];
if (actor != bait.Target && bait.Shape.Check(actor.Position, BaitOrigin(bait), bait.Rotation))
result.Add(actor);
}

return result;
}

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (!EnableHints)
return;

var count = ActiveBaits.Count;
if (count == 0)
return;
if (ForbiddenPlayers[slot])
{
if (ActiveBaitsOn(actor).Count != 0)
var activeBaits = ActiveBaitsOn(actor);
if (activeBaits.Count != 0)
hints.Add("Avoid baiting!");
}
else
{
if (ActiveBaitsOn(actor).Any(b => PlayersClippedBy(b).Any()))
hints.Add(BaitAwayHint);
var activeBaits = ActiveBaitsOn(actor);
for (var i = 0; i < activeBaits.Count; ++i)
{
var clippedPlayers = PlayersClippedBy(activeBaits[i]);
if (clippedPlayers.Count != 0)
{
hints.Add(BaitAwayHint);
break;
}
}
}

if (!IgnoreOtherBaits && ActiveBaitsNotOn(actor).Any(b => IsClippedBy(actor, b)))
hints.Add("GTFO from baited aoe!");
if (!IgnoreOtherBaits)
{
var otherActiveBaits = ActiveBaitsNotOn(actor);
for (var i = 0; i < otherActiveBaits.Count; ++i)
{
if (IsClippedBy(actor, otherActiveBaits[i]))
{
hints.Add("GTFO from baited aoe!");
break;
}
}
}
}

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (ActiveBaits.Count == 0)
return;
var activeBaitsNotOnActor = ActiveBaitsNotOn(actor);
var activeBaitsOnActor = ActiveBaitsOn(actor);
var countActiveBaitsNotOnActor = activeBaitsNotOnActor.Count;
var countActiveBaitsOnActor = activeBaitsOnActor.Count;

foreach (var bait in ActiveBaitsNotOn(actor))
for (var i = 0; i < countActiveBaitsNotOnActor; ++i)
{
var bait = activeBaitsNotOnActor[i];
hints.AddForbiddenZone(bait.Shape, BaitOrigin(bait), bait.Rotation, bait.Activation);

foreach (var bait in ActiveBaitsOn(actor))
AddTargetSpecificHints(actor, bait, hints);
}
for (var i = 0; i < countActiveBaitsOnActor; ++i)
{
AddTargetSpecificHints(actor, activeBaitsOnActor[i], hints);
}
}

private void AddTargetSpecificHints(Actor actor, Bait bait, AIHints hints)
Expand Down
16 changes: 13 additions & 3 deletions BossMod/Components/Towers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
}

// for tower mechanics in open world since likely not everyone is in your party
public class GenericTowersOpenWorld(BossModule module, ActionID aid = default, bool prioritizeInsufficient = false) : CastCounter(module, aid)
public class GenericTowersOpenWorld(BossModule module, ActionID aid = default, bool prioritizeInsufficient = false, bool prioritizeEmpty = false) : CastCounter(module, aid)
{
public struct Tower(WPos position, float radius, int minSoakers = 1, int maxSoakers = 1, HashSet<Actor>? allowedSoakers = null, DateTime activation = default)
{
Expand Down Expand Up @@ -315,6 +315,7 @@ public int NumInside(BossModule module)

public readonly List<Tower> Towers = [];
public readonly bool PrioritizeInsufficient = prioritizeInsufficient; // give priority to towers with more than 0 but less than min soakers
public readonly bool PrioritizeEmpty = prioritizeEmpty; // give priority to towers with 0 soakers

// default tower styling
public static void DrawTower(MiniArena arena, WPos pos, float radius, bool safe)
Expand Down Expand Up @@ -420,7 +421,16 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
}
if (!hasForbiddenSoakers)
{
if (PrioritizeInsufficient)
if (PrioritizeEmpty)
{
for (var i = 0; i < count; ++i)
{
var t = Towers[i];
if (t.NumInside(Module) == 0)
forbiddenInverted.Add(ShapeDistance.InvertedCircle(t.Position, t.Radius));
}
}
else if (PrioritizeInsufficient) // less soakers than max
{
List<Tower> insufficientTowers = new(count);
for (var i = 0; i < count; ++i)
Expand Down Expand Up @@ -510,7 +520,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
}
}

public class CastTowersOpenWorld(BossModule module, ActionID aid, float radius, int minSoakers = 1, int maxSoakers = 1) : GenericTowersOpenWorld(module, aid)
public class CastTowersOpenWorld(BossModule module, ActionID aid, float radius, int minSoakers = 1, int maxSoakers = 1, bool prioritizeInsufficient = false, bool prioritizeEmpty = false) : GenericTowersOpenWorld(module, aid, prioritizeInsufficient, prioritizeEmpty)
{
public readonly float Radius = radius;
public readonly int MinSoakers = minSoakers;
Expand Down
56 changes: 48 additions & 8 deletions BossMod/Data/ActorEnumeration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,24 +100,64 @@ public static IEnumerable<Actor> InRadiusExcluding(this IEnumerable<Actor> range
}

// select actors in specified shape
public static IEnumerable<Actor> InShape(this IEnumerable<Actor> range, AOEShape shape, Actor origin)
public static List<Actor> InShape(this IEnumerable<Actor> range, AOEShape shape, Actor origin)
{
return range.Where(actor => shape.Check(actor.Position, origin));
List<Actor> result = [];

foreach (var actor in range)
{
if (shape.Check(actor.Position, origin))
{
result.Add(actor);
}
}

return result;
}

public static IEnumerable<(int, Actor)> InShape(this IEnumerable<(int, Actor)> range, AOEShape shape, Actor origin)
public static List<(int, Actor)> InShape(this IEnumerable<(int, Actor)> range, AOEShape shape, Actor origin)
{
return range.WhereActor(actor => shape.Check(actor.Position, origin));
List<(int, Actor)> result = [];

foreach (var tuple in range)
{
if (shape.Check(tuple.Item2.Position, origin))
{
result.Add(tuple);
}
}

return result;
}

public static IEnumerable<Actor> InShape(this IEnumerable<Actor> range, AOEShape shape, WPos origin, Angle rotation)
public static List<Actor> InShape(this IEnumerable<Actor> range, AOEShape shape, WPos origin, Angle rotation)
{
return range.Where(actor => shape.Check(actor.Position, origin, rotation));
List<Actor> result = [];

foreach (var actor in range)
{
if (shape.Check(actor.Position, origin, rotation))
{
result.Add(actor);
}
}

return result;
}

public static IEnumerable<(int, Actor)> InShape(this IEnumerable<(int, Actor)> range, AOEShape shape, WPos origin, Angle rotation)
public static List<(int, Actor)> InShape(this IEnumerable<(int, Actor)> range, AOEShape shape, WPos origin, Angle rotation)
{
return range.WhereActor(actor => shape.Check(actor.Position, origin, rotation));
List<(int, Actor)> result = [];

foreach (var tuple in range)
{
if (shape.Check(tuple.Item2.Position, origin, rotation))
{
result.Add(tuple);
}
}

return result;
}

// select actors that have tether with specific ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ public override void Update()
CurrentBaits.Clear();
var deadline = WorldState.FutureTime(7d);
foreach (var (i, p) in Raid.WithSlot(false, false, true))
if (_activation[i] != default && _activation[i] < deadline)
CurrentBaits.Add(new(p, p, _shape, _activation[i]));
{
ref var activation = ref _activation[i];
if (activation != default && activation < deadline)
CurrentBaits.Add(new(p, p, _shape, activation));
}
}

public override void OnStatusGain(Actor actor, ActorStatus status)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ public override void Update()

for (var i = 0; i < 4; ++i)
{
var aoe = aoeChecks[i];
if (ActiveAOEs(0, Raid.Player()!).Any(c => c.Shape == aoe.AOE && c.Activation <= WorldState.CurrentTime))
var aoeCheck = aoeChecks[i];
if (_aoe is AOEInstance aoe && aoe.Shape == aoeCheck.AOE && aoe.Activation <= WorldState.CurrentTime)
{
Arena.Bounds = aoe.Bounds;
Arena.Bounds = aoeCheck.Bounds;
_aoe = null;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public enum IconID : uint
}

class CandescentRayLineStack(BossModule module) : Components.LineStack(module, null, ActionID.MakeSpell(AID.CandescentRayLineStack), minStackSize: 3, maxStackSize: 3);
class CandescentRayTB(BossModule module) : Components.CastSharedTankbuster(module, ActionID.MakeSpell(AID.CandescentRayTB), new AOEShapeRect(50, 4))
class CandescentRayTB(BossModule module) : Components.CastSharedTankbuster(module, ActionID.MakeSpell(AID.CandescentRayTB), new AOEShapeRect(50f, 4f))
{
public override void AddHints(int slot, Actor actor, TextHints hints)
{
Expand All @@ -70,19 +70,21 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
{
if (Target == null)
return;
var koana = Module.Enemies(OID.Koana).FirstOrDefault();
var wuk = Module.Enemies(OID.WukLamat).FirstOrDefault();
var koanas = Module.Enemies(OID.Koana);
var wuks = Module.Enemies(OID.WukLamat);
var koana = koanas.Count != 0 ? koanas[0] : null;
var wuk = wuks.Count != 0 ? wuks[0] : null;
var primary = Module.PrimaryActor;
if (koana != null)
hints.AddForbiddenZone(ShapeDistance.Cone(primary.Position, 100, primary.AngleTo(koana), Angle.Asin(4 / (koana.Position - primary.Position).Length())), Activation);
hints.AddForbiddenZone(ShapeDistance.Cone(primary.Position, 100f, primary.AngleTo(koana), Angle.Asin(4f / (koana.Position - primary.Position).Length())), Activation);
if (wuk != null)
hints.AddForbiddenZone(ShapeDistance.InvertedRect(primary.Position, primary.AngleTo(wuk), 50, 0, 4), Activation);
hints.AddForbiddenZone(ShapeDistance.InvertedRect(primary.Position, primary.AngleTo(wuk), 50f, default, 4f), Activation);
}
}

class SearingSwell(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SearingSwell), new AOEShapeCone(40, 22.5f.Degrees()));
class Ensnare(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Ensnare), 6);
class TriceraSnare(BossModule module) : Components.SpreadFromIcon(module, (uint)IconID.Spreadmarker, ActionID.MakeSpell(AID.TriceraSnare), 6, 4.7f)
class SearingSwell(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SearingSwell), new AOEShapeCone(40f, 22.5f.Degrees()));
class Ensnare(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Ensnare), 6f);
class TriceraSnare(BossModule module) : Components.SpreadFromIcon(module, (uint)IconID.Spreadmarker, ActionID.MakeSpell(AID.TriceraSnare), 6f, 4.7f)
{
public override void OnEventDirectorUpdate(uint updateID, uint param1, uint param2, uint param3, uint param4)
{
Expand All @@ -96,7 +98,7 @@ class PrimordialRoar2(BossModule module) : Components.RaidwideCast(module, Actio

class OrbCollecting(BossModule module) : BossComponent(module)
{
private readonly List<Actor> _orbs = module.Enemies(OID.Orbs);
private readonly List<Actor> _orbs = module.Enemies((uint)OID.Orbs);

private IEnumerable<Actor> ActiveOrbs => _orbs.Where(x => x.Tether.ID != 0);

Expand Down Expand Up @@ -132,9 +134,9 @@ public override void DrawArenaForeground(int pcSlot, Actor pc)

class FlameBlast(BossModule module) : Components.GenericAOEs(module)
{
private static readonly AOEShapeRect rect = new(20, 2, 20);
private static readonly AOEShapeRect rect = new(20f, 2f, 20f);
private readonly List<AOEInstance> _aoes = [];
private static readonly Angle a90 = 90.Degrees();
private static readonly Angle a90 = 90f.Degrees();

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
Expand All @@ -155,13 +157,13 @@ public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)

public override void OnActorCreated(Actor actor)
{
if ((OID)actor.OID == OID.BallOfFire)
_aoes.Add(new(rect, actor.Position, actor.Rotation + a90, WorldState.FutureTime(6.7f)));
if (actor.OID == (uint)OID.BallOfFire)
_aoes.Add(new(rect, WPos.ClampToGrid(actor.Position), actor.Rotation + a90, WorldState.FutureTime(6.7d)));
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.FlameBlast)
if (_aoes.Count != 0 && spell.Action.ID == (uint)AID.FlameBlast)
_aoes.RemoveAt(0);
}
}
Expand All @@ -185,13 +187,13 @@ public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.Firestorm)
if (spell.Action.ID == (uint)AID.Firestorm)
_aoes.Add(new(circle, caster.Position, spell.Rotation, Module.CastFinishAt(spell)));
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.Firestorm)
if (_aoes.Count != 0 && spell.Action.ID == (uint)AID.Firestorm)
_aoes.RemoveAt(0);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
if (count == 0)
return [];
var aoes = new AOEInstance[count];
for (var i = 0; i < _hearts.Count; ++i)
for (var i = 0; i < count; ++i)
{
var h = _hearts[i];
aoes[i] = new(capsule, h.Position, h.Rotation);
Expand Down
Loading

0 comments on commit c936477

Please sign in to comment.