Skip to content

Commit

Permalink
Merge pull request #573 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
refactor livia module
  • Loading branch information
CarnifexOptimus authored Jan 23, 2025
2 parents 9bb3517 + 504b6ae commit 1b47e4a
Show file tree
Hide file tree
Showing 30 changed files with 308 additions and 417 deletions.
25 changes: 11 additions & 14 deletions BossMod/BossModule/ArenaBounds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,11 @@ public record class ArenaBoundsCustom : ArenaBounds
private Pathfinding.Map? _cachedMap;
public readonly RelSimplifiedComplexPolygon poly;
private readonly (WDir, WDir)[] edges;
private readonly float offset;
public float HalfWidth, HalfHeight;

public ArenaBoundsCustom(float Radius, RelSimplifiedComplexPolygon Poly, float MapResolution = Half, float Offset = 0, float ScaleFactor = 1)
public ArenaBoundsCustom(float Radius, RelSimplifiedComplexPolygon Poly, float MapResolution = Half, float ScaleFactor = 1)
: base(Radius, MapResolution, ScaleFactor)
{
offset = Offset;
poly = Poly;

var edgeList = new List<(WDir, WDir)>();
Expand Down Expand Up @@ -257,7 +255,7 @@ public override WDir ClampToBounds(WDir offset)
var nearestPoint = offset;
for (var i = 0; i < edges.Length; ++i)
{
var edge = edges[i];
ref var edge = ref edges[i];
var segmentVector = edge.Item2 - edge.Item1;
var segmentLengthSq = segmentVector.LengthSq();
var t = Math.Max(0, Math.Min(1, (offset - edge.Item1).Dot(segmentVector) / segmentLengthSq));
Expand All @@ -277,8 +275,7 @@ public override WDir ClampToBounds(WDir offset)

private Pathfinding.Map BuildMap()
{
// faster than using the polygonwithholes distance method directly
var polygon = offset != 0 ? poly.Offset(offset) : poly;
var polygon = poly;
if (HalfHeight == default) // calculate bounding box if not already done by ArenaBoundsComplex to reduce amount of point in polygon tests
{
float minX = float.MaxValue, maxX = float.MinValue, minZ = float.MaxValue, maxZ = float.MinValue;
Expand Down Expand Up @@ -307,9 +304,9 @@ private Pathfinding.Map BuildMap()
var width = map.Width;
var height = map.Height;
var resolution = map.Resolution;
var center = map.Center;
ref var center = ref map.Center;

var halfSample = MapResolution * Half - Epsilon; // tiny offset to account for floating point inaccuracies
var halfSample = resolution * Half - Epsilon; // tiny offset to account for floating point inaccuracies

WDir[] sampleOffsets =
[
Expand Down Expand Up @@ -364,21 +361,21 @@ public sealed record class ArenaBoundsComplex : ArenaBoundsCustom
public readonly WPos Center;
public bool IsCircle; // can be used by gaze component for gazes outside of the arena

public ArenaBoundsComplex(Shape[] UnionShapes, Shape[]? DifferenceShapes = null, Shape[]? AdditionalShapes = null, float MapResolution = Half, float Offset = 0, float ScaleFactor = 1)
: base(BuildBounds(UnionShapes, DifferenceShapes, AdditionalShapes, MapResolution, Offset, ScaleFactor, out var center, out var halfWidth, out var halfHeight))
public ArenaBoundsComplex(Shape[] UnionShapes, Shape[]? DifferenceShapes = null, Shape[]? AdditionalShapes = null, float MapResolution = Half, float ScaleFactor = 1)
: base(BuildBounds(UnionShapes, DifferenceShapes, AdditionalShapes, MapResolution, ScaleFactor, out var center, out var halfWidth, out var halfHeight))
{
Center = center;
HalfWidth = halfWidth + Offset;
HalfHeight = halfHeight + Offset;
HalfWidth = halfWidth;
HalfHeight = halfHeight;
}

private static ArenaBoundsCustom BuildBounds(Shape[] unionShapes, Shape[]? differenceShapes, Shape[]? additionalShapes, float mapResolution, float offset, float scalefactor, out WPos center, out float halfWidth, out float halfHeight)
private static ArenaBoundsCustom BuildBounds(Shape[] unionShapes, Shape[]? differenceShapes, Shape[]? additionalShapes, float mapResolution, float scalefactor, out WPos center, out float halfWidth, out float halfHeight)
{
var properties = CalculatePolygonProperties(unionShapes, differenceShapes ?? [], additionalShapes ?? []);
center = properties.Center;
halfWidth = properties.HalfWidth;
halfHeight = properties.HalfHeight;
return new(scalefactor == 1 ? properties.Radius : properties.Radius / scalefactor, properties.Poly, mapResolution, offset, scalefactor);
return new(scalefactor == 1 ? properties.Radius : properties.Radius / scalefactor, properties.Poly, mapResolution, scalefactor);
}

private static (WPos Center, float HalfWidth, float HalfHeight, float Radius, RelSimplifiedComplexPolygon Poly) CalculatePolygonProperties(Shape[] unionShapes, Shape[] differenceShapes, Shape[] additionalShapes)
Expand Down
2 changes: 1 addition & 1 deletion BossMod/BossModule/BossModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public void Draw(Angle cameraAzimuth, int pcSlot, bool includeText, bool include
}
if (includeArena)
{
_ = Arena.Begin(cameraAzimuth);
Arena.Begin(cameraAzimuth);
DrawArena(pcSlot, pc, pcHints.Any(h => h.Item2));
MiniArena.End();
}
Expand Down
9 changes: 2 additions & 7 deletions BossMod/BossModule/MiniArena.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public ArenaBounds Bounds
public float IntersectRayBounds(WPos rayOrigin, WDir rayDir) => _bounds.IntersectRay(rayOrigin - _center, rayDir);

// prepare for drawing - set up internal state, clip rect etc.
public async Task Begin(Angle cameraAzimuth)
public void Begin(Angle cameraAzimuth)
{
var centerOffset = new Vector2(ScreenMarginSize + Config.SlackForRotations * ScreenHalfSize);
var fullSize = 2 * centerOffset;
Expand Down Expand Up @@ -86,14 +86,9 @@ public async Task Begin(Angle cameraAzimuth)

if (Config.OpaqueArenaBackground)
{
await GenerateBackgroundAsync().ConfigureAwait(true);
Zone(_bounds.ShapeTriangulation, Colors.Background);
}
}
private Task GenerateBackgroundAsync()
{
Zone(_bounds.ShapeTriangulation, Colors.Background);
return Task.CompletedTask;
}

// if you are 100% sure your primitive does not need clipping, you can use drawlist api directly
// this helper allows converting world-space coords to screen-space ones
Expand Down
9 changes: 4 additions & 5 deletions BossMod/Components/ConcentricAOEs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,22 @@ public struct Sequence
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var count = Sequences.Count;
List<AOEInstance> activeAOEs = new(count);
var aoes = new AOEInstance[count];
for (var i = 0; i < count; ++i)
{
var s = Sequences[i];
if (s.NumCastsDone < Shapes.Length)
{
if (!showall)
activeAOEs.Add(new(Shapes[s.NumCastsDone], s.Origin, s.Rotation, s.NextActivation));
aoes[i] = new(Shapes[s.NumCastsDone], s.Origin, s.Rotation, s.NextActivation);
else
{
for (var j = s.NumCastsDone; j < Shapes.Length; ++j)
activeAOEs.Add(new(Shapes[j], s.Origin, s.Rotation, s.NextActivation));
aoes[i] = new(Shapes[j], s.Origin, s.Rotation, s.NextActivation);
}
}
}

return activeAOEs;
return aoes;
}

public void AddSequence(WPos origin, DateTime activation = default, Angle rotation = default) => Sequences.Add(new() { Origin = origin, Rotation = rotation, NextActivation = activation });
Expand Down
15 changes: 7 additions & 8 deletions BossMod/Components/Exaflare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,31 @@ public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
var futureAOEs = FutureAOEs(linesCount);
var imminentAOEs = ImminentAOEs(linesCount);
var futureCount = futureAOEs.Count;
var imminentCount = imminentAOEs.Count;
var imminentCount = imminentAOEs.Length;

List<AOEInstance> aoes = new(futureCount + imminentCount);
var aoes = new AOEInstance[futureCount + imminentCount];
for (var i = 0; i < futureCount; ++i)
{
var aoe = futureAOEs[i];
aoes.Add(new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, FutureColor));
aoes[i] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, FutureColor);
}

for (var i = 0; i < imminentCount; ++i)
{
var aoe = imminentAOEs[i];
aoes.Add(new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, ImminentColor));
aoes[futureCount + i] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, ImminentColor);
}
return aoes;
}

protected List<(WPos, DateTime, Angle)> ImminentAOEs(int count)
protected (WPos, DateTime, Angle)[] ImminentAOEs(int count)
{
var exas = new List<(WPos, DateTime, Angle)>(count);

var exas = new (WPos, DateTime, Angle)[count];
for (var i = 0; i < count; ++i)
{
var l = Lines[i];
if (l.ExplosionsLeft != 0)
exas.Add((l.Next, l.NextExplosion, l.Rotation));
exas[i] = (l.Next, l.NextExplosion, l.Rotation);
}
return exas;
}
Expand Down
11 changes: 7 additions & 4 deletions BossMod/Components/GenericAOEs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public List<AOEInstance> ActiveCasters
List<AOEInstance> aoes = new(max);
for (var i = 0; i < max; ++i)
{
aoes.Add(Casters[i]);
aoes[i] = Casters[i];
}
return aoes;
}
Expand All @@ -73,16 +73,19 @@ public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
var count = Casters.Count;
if (count == 0)
return [];

var time = WorldState.CurrentTime;
var max = count > MaxCasts ? MaxCasts : count;
List<AOEInstance> aoes = new(max);

var aoes = new AOEInstance[max];
for (var i = 0; i < max; ++i)
{
var caster = Casters[i];
var color = i < MaxDangerColor && count > MaxDangerColor ? Colors.Danger : 0;
var risky = Risky && (MaxRisky == null || i < MaxRisky);
aoes.Add(RiskyWithSecondsLeft == 0 ? caster with { Color = color, Risky = risky }
: caster with { Color = color, Risky = risky && caster.Activation.AddSeconds(-RiskyWithSecondsLeft) <= time });
aoes[i] = RiskyWithSecondsLeft == 0
? caster with { Color = color, Risky = risky }
: caster with { Color = color, Risky = risky && caster.Activation.AddSeconds(-RiskyWithSecondsLeft) <= time };
}
return aoes;
}
Expand Down
84 changes: 64 additions & 20 deletions BossMod/Data/PartyState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,36 +52,80 @@ void assign(ulong instanceID, Actor? actor)
public Actor[] WithoutSlot(bool includeDead = false, bool excludeAlliance = false, bool excludeNPCs = false)
{
var limit = excludeNPCs ? MaxAllianceSize : MaxAllies;
var result = new List<Actor>(limit);
for (var i = 0; i < limit; ++i)
var result = new Actor[limit];
var count = 0;

if (excludeAlliance)
{
if (excludeAlliance && i >= MaxPartySize && i < MaxAllianceSize)
continue;
for (var i = 0; i < MaxPartySize; ++i)
{
ref var player = ref _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;

result[count++] = player;
}
if (!excludeNPCs)
for (var i = MaxAllianceSize; i < limit; ++i)
{
ref var player = ref _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;

var player = _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;
result.Add(player);
result[count++] = player;
}
}
return [.. result];
else
{
for (var i = 0; i < limit; ++i)
{
ref var player = ref _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;

result[count++] = player;
}
}
return result[..count];
}

public (int, Actor)[] WithSlot(bool includeDead = false, bool excludeAlliance = false, bool excludeNPCs = false)
{
var limit = excludeNPCs ? MaxAllianceSize : MaxAllies;
var result = new List<(int, Actor)>(limit);
for (var i = 0; i < limit; ++i)
var result = new (int, Actor)[limit];
var count = 0;

if (excludeAlliance)
{
if (excludeAlliance && i is >= MaxPartySize and < MaxAllianceSize)
continue;
var player = _actors[i];
if (player == null)
continue;
if (!includeDead && player.IsDead)
continue;
result.Add((i, player));
for (var i = 0; i < MaxPartySize; ++i)
{
ref var player = ref _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;

result[count++] = (i, player);
}
for (var i = MaxAllianceSize; i < limit; ++i)
{
ref var player = ref _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;

result[count++] = (i, player);
}
}
else
{
for (var i = 0; i < limit; ++i)
{
ref var player = ref _actors[i];
if (player == null || !includeDead && player.IsDead)
continue;

result[count++] = (i, player);
}
}
return [.. result];
return result[..count];
}

// find a slot index containing specified player (by instance ID); returns -1 if not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ class DecisiveBattle(BossModule module) : BossComponent(module)

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (slot < PartyState.MaxAllianceSize && AssignedBoss[slot] != null)
ref var assignedSlot = ref AssignedBoss[slot];
if (slot < PartyState.MaxAllianceSize && assignedSlot != null)
{
var target = WorldState.Actors.Find(actor.TargetID);
if (target != null && target != AssignedBoss[slot] && (OID)target.OID is OID.BossMR or OID.BossTT or OID.BossGK)
hints.Add($"Target {AssignedBoss[slot]?.Name}!");
if (target != null && target != assignedSlot && (OID)target.OID is OID.BossMR or OID.BossTT or OID.BossGK)
hints.Add($"Target {assignedSlot?.Name}!");
}
}

Expand Down Expand Up @@ -38,11 +39,12 @@ public override void OnStatusGain(Actor actor, ActorStatus status)

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (slot < AssignedBoss.Length && AssignedBoss[slot] != null)
ref var assignedSlot = ref AssignedBoss[slot];
if (slot < AssignedBoss.Length && assignedSlot != null)
for (var i = 0; i < hints.PotentialTargets.Count; ++i)
{
var enemy = hints.PotentialTargets[i];
if (enemy.Actor != AssignedBoss[slot])
if (enemy.Actor != assignedSlot)
enemy.Priority = AIHints.Enemy.PriorityInvincible;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
var imminentAOEs = ImminentAOEs(linesCount);

// use only imminent aoes for hints
for (var i = 0; i < imminentAOEs.Count; ++i)
for (var i = 0; i < imminentAOEs.Length; ++i)
{
var aoe = imminentAOEs[i];
hints.AddForbiddenZone(Shape, aoe.Item1, aoe.Item3, aoe.Item2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
if (!_config.ShowOccupiedTiles)
return [];
var cell = CellIndex(actor.Position - Arena.Center) - 3;
List<AOEInstance> tiles = new(16); // 3 * 4 + 4 margin for error
List<AOEInstance> tiles = new(16); // 3 * 4 players + 4 margin for error/evil seeds
for (var i = 0; i < 28; ++i)
{
if (_breakTime[i] != default)
ref var breaktime = ref _breakTime[i];
if (breaktime != default)
{
if (i == cell)
{
if ((_breakTime[i] - WorldState.CurrentTime).TotalSeconds < 6)
if ((breaktime - WorldState.CurrentTime).TotalSeconds < 6)
tiles.Add(new(square, CellCenter(i)));
}
else
Expand Down
Loading

0 comments on commit 1b47e4a

Please sign in to comment.