Skip to content

Commit 67e95f9

Browse files
Merge pull request #944 from FFXIV-CombatReborn/mergeWIP
Faithbound Kirin module, improved Necron Aetherblight hint
2 parents 126a820 + 830a7f1 commit 67e95f9

File tree

37 files changed

+927
-212
lines changed

37 files changed

+927
-212
lines changed

BossMod/Autorotation/MiscAI/AutoPull.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace BossMod.Autorotation.MiscAI;
55
// TODO this module is now useless and has been merged with AutoFarm, but some plugins still use it, like Questionable (rip)
66
public sealed class AutoPull(RotationModuleManager manager, Actor player) : RotationModule(manager, player)
77
{
8-
public enum Track { QuestBattle, DeepDungeon, EpicEcho, Hunt }
8+
public enum Track { QuestBattle, DeepDungeon, EpicEcho, Hunt, TreasureHunt }
99

1010
public static RotationModuleDefinition Definition()
1111
{
@@ -15,7 +15,7 @@ public static RotationModuleDefinition Definition()
1515
def.AbilityTrack(Track.DeepDungeon, "Automatically attack deep dungeon bosses when solo");
1616
def.AbilityTrack(Track.EpicEcho, "Automatically attack all targets if the Epic Echo status is present (i.e. when unsynced)");
1717
def.AbilityTrack(Track.Hunt, "Automatically attack hunt marks once they are below 95% HP");
18-
18+
def.AbilityTrack(Track.TreasureHunt, "Automatically attack treasure hunt bosses");
1919
return def;
2020
}
2121

@@ -37,11 +37,14 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa
3737
if (strategy.Enabled(Track.QuestBattle))
3838
enabled |= Bossmods.ActiveModule?.Info?.Category == BossModuleInfo.Category.Quest;
3939

40+
if (strategy.Enabled(Track.TreasureHunt))
41+
enabled |= Bossmods.ActiveModule?.Info?.Category == BossModuleInfo.Category.TreasureHunt;
42+
4043
if (strategy.Enabled(Track.DeepDungeon))
4144
enabled |= Bossmods.ActiveModule?.Info?.Category == BossModuleInfo.Category.DeepDungeon && World.Party.WithoutSlot().Length == 1;
4245

4346
if (strategy.Enabled(Track.EpicEcho))
44-
enabled |= Player.Statuses.Any(s => s.ID == 2734);
47+
enabled |= Player.Statuses.Any(s => s.ID == 2734u);
4548

4649
if (enabled)
4750
{

BossMod/BossModule/ArenaBounds.cs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -385,30 +385,30 @@ public sealed record class ArenaBoundsComplex : ArenaBoundsCustom
385385
public readonly WPos Center;
386386
public bool IsCircle; // can be used by gaze component for gazes outside of the arena
387387

388-
public ArenaBoundsComplex(Shape[] UnionShapes, Shape[]? DifferenceShapes = null, Shape[]? AdditionalShapes = null, float MapResolution = 0.5f, float ScaleFactor = 1f, bool AllowObstacleMap = false, float Offset = default)
389-
: base(BuildBounds(UnionShapes, DifferenceShapes, AdditionalShapes, MapResolution, ScaleFactor, AllowObstacleMap, Offset, out var center, out var halfWidth, out var halfHeight))
388+
public ArenaBoundsComplex(Shape[] UnionShapes, Shape[]? DifferenceShapes = null, Shape[]? AdditionalShapes = null, float MapResolution = 0.5f, float ScaleFactor = 1f, bool AllowObstacleMap = false, float Offset = default, bool AdjustForHitbox = false)
389+
: base(BuildBounds(UnionShapes, DifferenceShapes, AdditionalShapes, MapResolution, ScaleFactor, AllowObstacleMap, Offset, AdjustForHitbox, out var center, out var halfWidth, out var halfHeight))
390390
{
391391
Center = center;
392392
HalfWidth = halfWidth + Offset;
393393
HalfHeight = halfHeight + Offset;
394394
}
395395

396-
private static ArenaBoundsCustom BuildBounds(Shape[] unionShapes, Shape[]? differenceShapes, Shape[]? additionalShapes, float mapResolution, float scalefactor, bool allowObstacleMap, float offset, out WPos center, out float halfWidth, out float halfHeight)
396+
private static ArenaBoundsCustom BuildBounds(Shape[] unionShapes, Shape[]? differenceShapes, Shape[]? additionalShapes, float mapResolution, float scalefactor, bool allowObstacleMap, float offset, bool adjustForHitbox, out WPos center, out float halfWidth, out float halfHeight)
397397
{
398-
var properties = CalculatePolygonProperties(unionShapes, differenceShapes ?? [], additionalShapes ?? []);
398+
var properties = CalculatePolygonProperties(unionShapes, differenceShapes ?? [], additionalShapes ?? [], adjustForHitbox);
399399
center = properties.Center;
400400
halfWidth = properties.HalfWidth;
401401
halfHeight = properties.HalfHeight;
402-
return new(scalefactor == 1 ? properties.Radius : properties.Radius / scalefactor, properties.Poly, mapResolution, scalefactor, allowObstacleMap, offset);
402+
return new(scalefactor == 1f ? properties.Radius : properties.Radius / scalefactor, properties.Poly, mapResolution, scalefactor, allowObstacleMap, offset);
403403
}
404404

405-
private static (WPos Center, float HalfWidth, float HalfHeight, float Radius, RelSimplifiedComplexPolygon Poly) CalculatePolygonProperties(Shape[] unionShapes, Shape[] differenceShapes, Shape[] additionalShapes)
405+
private static (WPos Center, float HalfWidth, float HalfHeight, float Radius, RelSimplifiedComplexPolygon Poly) CalculatePolygonProperties(Shape[] unionShapes, Shape[] differenceShapes, Shape[] additionalShapes, bool adjustForHitbox)
406406
{
407407
var unionPolygons = ParseShapes(unionShapes);
408408
var differencePolygons = ParseShapes(differenceShapes);
409409
var additionalPolygons = ParseShapes(additionalShapes);
410-
411-
var combinedPoly = CombinePolygons(unionPolygons, differencePolygons, additionalPolygons);
410+
var combine = CombinePolygons(unionPolygons, differencePolygons, additionalPolygons);
411+
var combinedPoly = adjustForHitbox ? combine.Offset(-0.5f, Clipper2Lib.JoinType.Round) : combine;
412412

413413
float minX = float.MaxValue, maxX = float.MinValue, minZ = float.MaxValue, maxZ = float.MinValue;
414414
var combined = combinedPoly.Parts;
@@ -440,9 +440,20 @@ private static (WPos Center, float HalfWidth, float HalfHeight, float Radius, Re
440440
var maxDistZ = Math.Max(Math.Abs(maxZ - centerZ), Math.Abs(minZ - centerZ));
441441
var halfWidth = (maxX - minX) * 0.5f;
442442
var halfHeight = (maxZ - minZ) * 0.5f;
443+
var dir = center.ToWDir();
444+
445+
for (var i = 0; i < countCombined; ++i)
446+
{
447+
var verts = CollectionsMarshal.AsSpan(combined[i].Vertices);
448+
var len = verts.Length;
449+
for (var j = 0; j < len; ++j)
450+
{
451+
ref var vert = ref verts[j];
452+
vert -= dir;
453+
}
454+
}
443455

444-
var combinedPolyCentered = CombinePolygons(ParseShapes(unionShapes, center), ParseShapes(differenceShapes, center), ParseShapes(additionalShapes, center));
445-
return (center, halfWidth, halfHeight, Math.Max(maxDistX, maxDistZ), combinedPolyCentered);
456+
return (center, halfWidth, halfHeight, Math.Max(maxDistX, maxDistZ), combinedPoly);
446457
}
447458

448459
private static RelSimplifiedComplexPolygon CombinePolygons(RelSimplifiedComplexPolygon[] unionPolygons, RelSimplifiedComplexPolygon[] differencePolygons, RelSimplifiedComplexPolygon[] secondUnionPolygons)
@@ -469,13 +480,13 @@ private static RelSimplifiedComplexPolygon CombinePolygons(RelSimplifiedComplexP
469480
return combinedShape;
470481
}
471482

472-
private static RelSimplifiedComplexPolygon[] ParseShapes(Shape[] shapes, WPos center = default)
483+
private static RelSimplifiedComplexPolygon[] ParseShapes(Shape[] shapes)
473484
{
474485
var lenght = shapes.Length;
475486
var polygons = new RelSimplifiedComplexPolygon[lenght];
476487
for (var i = 0; i < lenght; ++i)
477488
{
478-
polygons[i] = shapes[i].ToPolygon(center);
489+
polygons[i] = shapes[i].ToPolygon(default);
479490
}
480491
return polygons;
481492
}

BossMod/Components/ConcentricAOEs.cs

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,44 @@ public struct Sequence
1515

1616
public readonly AOEShape[] Shapes = shapes;
1717
public readonly List<Sequence> Sequences = [];
18+
protected readonly List<AOEInstance> _aoes = [];
19+
protected int lastVersion, lastCount;
1820

19-
public override ReadOnlySpan<AOEInstance> ActiveAOEs(int slot, Actor actor)
21+
public override ReadOnlySpan<AOEInstance> ActiveAOEs(int slot, Actor actor) => CollectionsMarshal.AsSpan(_aoes);
22+
23+
public override void Update()
2024
{
2125
var count = Sequences.Count;
22-
if (count == 0)
26+
if (lastCount != count || lastVersion != NumCasts)
2327
{
24-
return [];
25-
}
26-
var aoes = new AOEInstance[count];
27-
var time = WorldState.CurrentTime;
28-
var sequences = CollectionsMarshal.AsSpan(Sequences);
29-
30-
for (var i = 0; i < count; ++i)
31-
{
32-
ref var s = ref sequences[i];
33-
var risky = true;
34-
var act = s.NextActivation;
35-
if (RiskyWithSecondsLeft != default)
36-
{
37-
risky = act.AddSeconds(-RiskyWithSecondsLeft) <= time;
38-
}
39-
if (!showall)
40-
{
41-
aoes[i] = new(Shapes[s.NumCastsDone], s.Origin, s.Rotation, act, risky: risky);
42-
}
43-
else
28+
var time = WorldState.CurrentTime;
29+
var sequences = CollectionsMarshal.AsSpan(Sequences);
30+
_aoes.Clear();
31+
for (var i = 0; i < count; ++i)
4432
{
45-
var len = Shapes.Length;
46-
for (var j = s.NumCastsDone; j < len; ++j)
33+
ref var s = ref sequences[i];
34+
var risky = true;
35+
var act = s.NextActivation;
36+
if (RiskyWithSecondsLeft != default)
37+
{
38+
risky = act.AddSeconds(-RiskyWithSecondsLeft) <= time;
39+
}
40+
if (!showall)
41+
{
42+
_aoes.Add(new(Shapes[s.NumCastsDone], s.Origin, s.Rotation, act, risky: risky));
43+
}
44+
else
4745
{
48-
aoes[i] = new(Shapes[j], s.Origin, s.Rotation, act, risky: risky);
46+
var len = Shapes.Length;
47+
for (var j = s.NumCastsDone; j < len; ++j)
48+
{
49+
_aoes.Add(new(Shapes[j], s.Origin, s.Rotation, act, risky: risky));
50+
}
4951
}
5052
}
53+
lastCount = count;
54+
lastVersion = NumCasts;
5155
}
52-
return aoes;
5356
}
5457

5558
public void AddSequence(WPos origin, DateTime activation = default, Angle rotation = default) => Sequences.Add(new() { Origin = origin, Rotation = rotation, NextActivation = activation });

BossMod/Components/Exaflare.cs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,40 @@ public sealed class Line(WPos next, WDir advance, DateTime nextExplosion, double
1818
public uint ImminentColor = Colors.Danger;
1919
public uint FutureColor = Colors.AOE;
2020
public readonly List<Line> Lines = [];
21+
protected AOEInstance[] _aoes = [];
22+
protected int currentVersion, lastVersion, lastCount;
2123

2224
public bool Active => Lines.Count != 0;
2325

2426
public Exaflare(BossModule module, float radius, uint aid = default) : this(module, new AOEShapeCircle(radius), aid) { }
2527

26-
public override ReadOnlySpan<AOEInstance> ActiveAOEs(int slot, Actor actor)
28+
public override ReadOnlySpan<AOEInstance> ActiveAOEs(int slot, Actor actor) => _aoes;
29+
30+
public override void Update()
2731
{
2832
var linesCount = Lines.Count;
29-
if (linesCount == 0)
30-
return [];
31-
var futureAOEs = FutureAOEs(linesCount);
32-
var imminentAOEs = ImminentAOEs(linesCount);
33-
var futureCount = futureAOEs.Count;
34-
var imminentCount = imminentAOEs.Length;
35-
36-
var aoes = new AOEInstance[futureCount + imminentCount];
37-
for (var i = 0; i < futureCount; ++i)
33+
if (lastCount != linesCount || currentVersion != lastVersion)
3834
{
39-
var aoe = futureAOEs[i];
40-
aoes[i] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, FutureColor);
41-
}
35+
var futureAOEs = CollectionsMarshal.AsSpan(FutureAOEs(linesCount));
36+
var imminentAOEs = ImminentAOEs(linesCount);
37+
var futureLen = futureAOEs.Length;
38+
var imminentLen = imminentAOEs.Length;
4239

43-
for (var i = 0; i < imminentCount; ++i)
44-
{
45-
var aoe = imminentAOEs[i];
46-
aoes[futureCount + i] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, ImminentColor);
40+
_aoes = new AOEInstance[futureLen + imminentLen];
41+
for (var i = 0; i < futureLen; ++i)
42+
{
43+
ref var aoe = ref futureAOEs[i];
44+
_aoes[i] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, FutureColor);
45+
}
46+
47+
for (var i = 0; i < imminentLen; ++i)
48+
{
49+
ref var aoe = ref imminentAOEs[i];
50+
_aoes[futureLen + i] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, ImminentColor);
51+
}
52+
lastCount = linesCount;
53+
lastVersion = currentVersion;
4754
}
48-
return aoes;
4955
}
5056

5157
protected (WPos, DateTime, Angle)[] ImminentAOEs(int count)
@@ -87,6 +93,7 @@ protected void AdvanceLine(Line l, WPos pos)
8793
l.Next = pos + l.Advance;
8894
l.NextExplosion = WorldState.FutureTime(l.TimeToMove);
8995
--l.ExplosionsLeft;
96+
++currentVersion;
9097
}
9198
}
9299

BossMod/Components/UnavoidableDamage.cs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,21 +180,43 @@ public class SingleTargetInstant(BossModule module, uint aid, double delay = def
180180
public override void AddGlobalHints(GlobalHints hints)
181181
{
182182
if (Targets.Count != 0 && Hint.Length != 0)
183+
{
183184
hints.Add(Hint);
185+
}
184186
}
185187

186188
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
187189
{
188-
foreach (var t in Targets)
190+
var count = Targets.Count;
191+
if (count == 0)
192+
{
193+
return;
194+
}
195+
var targets = CollectionsMarshal.AsSpan(Targets);
196+
for (var i = 0; i < count; ++i)
197+
{
198+
ref var t = ref targets[i];
189199
hints.AddPredictedDamage(new BitMask().WithBit(t.slot), t.activation, damageType);
200+
}
190201
}
191202

192203
public override void OnEventCast(Actor caster, ActorCastEvent spell)
193204
{
194205
if (spell.Action.ID == WatchedAction)
195206
{
196207
++NumCasts;
197-
Targets.RemoveAll(t => t.instanceID == spell.MainTargetID);
208+
var targets = CollectionsMarshal.AsSpan(Targets);
209+
var len = targets.Length;
210+
var id = spell.MainTargetID;
211+
for (var i = 0; i < len; ++i)
212+
{
213+
ref var t = ref targets[i];
214+
if (t.instanceID == id)
215+
{
216+
Targets.RemoveAt(i);
217+
return;
218+
}
219+
}
198220
}
199221
}
200222
}
@@ -269,7 +291,18 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)
269291
if (id == AIDs[i])
270292
{
271293
++NumCasts;
272-
Targets.RemoveAll(t => t.instanceID == spell.MainTargetID);
294+
var targets = CollectionsMarshal.AsSpan(Targets);
295+
var lenT = targets.Length;
296+
var tid = spell.MainTargetID;
297+
for (var j = 0; j < lenT; ++j)
298+
{
299+
ref var t = ref targets[j];
300+
if (t.instanceID == tid)
301+
{
302+
Targets.RemoveAt(j);
303+
return;
304+
}
305+
}
273306
}
274307
}
275308
}

BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/Rampage.cs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,16 @@ namespace BossMod.Dawntrail.Alliance.A13ArkAngels;
33
sealed class Rampage(BossModule module) : Components.GenericAOEs(module)
44
{
55
public readonly List<AOEInstance> AOEs = new(5);
6-
76
private static readonly AOEShapeCircle _shapeLast = new(20f);
87

9-
public override ReadOnlySpan<AOEInstance> ActiveAOEs(int slot, Actor actor)
8+
public override ReadOnlySpan<AOEInstance> ActiveAOEs(int slot, Actor actor) => CollectionsMarshal.AsSpan(AOEs);
9+
10+
public override void Update()
1011
{
11-
var count = AOEs.Count;
12-
if (count == 0)
13-
return [];
14-
Span<AOEInstance> aoes = new AOEInstance[count];
15-
var color = Colors.Danger;
16-
for (var i = 0; i < count; ++i)
12+
if (AOEs.Count > 1)
1713
{
18-
var aoe = AOEs[i];
19-
aoes[i] = i == 0 ? count > 1 ? aoe with { Color = color } : aoe : aoe;
14+
AOEs.Ref(0).Color = Colors.Danger;
2015
}
21-
return aoes;
2216
}
2317

2418
public override void OnCastStarted(Actor caster, ActorCastInfo spell)
@@ -27,10 +21,10 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell)
2721
{
2822
case (uint)AID.RampagePreviewCharge:
2923
var toDest = spell.LocXZ - caster.Position;
30-
AOEs.Add(new(new AOEShapeRect(toDest.Length(), 5f), caster.Position, Angle.FromDirection(toDest), Module.CastFinishAt(spell, 5.1f + 0.2f * AOEs.Count)));
24+
AOEs.Add(new(new AOEShapeRect(toDest.Length(), 5f), caster.Position, Angle.FromDirection(toDest), Module.CastFinishAt(spell, 5.1d + 0.2d * AOEs.Count)));
3125
break;
3226
case (uint)AID.RampagePreviewLast:
33-
AOEs.Add(new(_shapeLast, spell.LocXZ, default, Module.CastFinishAt(spell, 6.3f)));
27+
AOEs.Add(new(_shapeLast, spell.LocXZ, default, Module.CastFinishAt(spell, 6.3d)));
3428
break;
3529
}
3630
}
@@ -41,7 +35,9 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)
4135
{
4236
++NumCasts;
4337
if (AOEs.Count != 0)
38+
{
4439
AOEs.RemoveAt(0);
40+
}
4541
}
4642
}
4743
}

BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLord.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
sealed class Teleport(BossModule module) : Components.CastCounter(module, (uint)AID.Teleport);
44
sealed class TeraSlash(BossModule module) : Components.CastCounter(module, (uint)AID.TeraSlash);
55
sealed class DoomArc(BossModule module) : Components.RaidwideCast(module, (uint)AID.DoomArc);
6-
sealed class UnbridledRage(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeRect(100f, 4f), (uint)IconID.UnbridledRage, (uint)AID.UnbridledRageAOE, 5.9f);
6+
sealed class UnbridledRage(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeRect(100f, 4f), (uint)IconID.UnbridledRage, (uint)AID.UnbridledRageAOE, 5.9d);
77
sealed class DarkNova(BossModule module) : Components.SpreadFromCastTargets(module, (uint)AID.DarkNova, 6f);
88

99
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13653, SortOrder = 8, PlanLevel = 100)]

0 commit comments

Comments
 (0)