Skip to content

Commit

Permalink
Merge branch 'merge' into mergeWIP
Browse files Browse the repository at this point in the history
  • Loading branch information
CarnifexOptimus committed Jan 24, 2025
2 parents f5a590c + 0fdf9ad commit 7b56453
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 24 deletions.
1 change: 1 addition & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRU.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class P5ParadiseLost(BossModule module) : Components.CastCounter(module, ActionI
public class FRU(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena with { IsCircle = true })
{
private static readonly ArenaBoundsComplex arena = new([new Polygon(new(100, 100), 20, 64)]);
public static readonly ArenaBoundsSquare PathfindHugBorderBounds = new(20); // this is a hack to allow precise positioning near border by some mechanics, TODO reconsider

private Actor? _bossP2;
private Actor? _iceVeil;
Expand Down
1 change: 1 addition & 0 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P1UtopianSky.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
_ => default
};
var range = spreadSpot == 0 ? 13 : 19;
hints.PathfindMapBounds = FRU.PathfindHugBorderBounds;
hints.AddForbiddenZone(ShapeDistance.PrecisePosition(Arena.Center + range * direction.ToDirection(), new(0, 1), Arena.Bounds.MapResolution, actor.Position, 0.1f), _aoes.Activation);
}
}
Expand Down
5 changes: 4 additions & 1 deletion BossMod/Modules/Dawntrail/Ultimate/FRU/P2DiamondDust.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ class P2DiamondDustSafespots(BossModule module) : BossComponent(module)
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (_safeOffs[slot] != default)
{
hints.PathfindMapBounds = FRU.PathfindHugBorderBounds;
hints.AddForbiddenZone(ShapeDistance.PrecisePosition(Arena.Center + _safeOffs[slot], new WDir(0, 1), Arena.Bounds.MapResolution, actor.Position, 0.1f));
}
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
Expand Down Expand Up @@ -299,7 +302,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
// non-healers should just stack with whatever closest healer is
// before first cast, ignore master's movements
var moveDir = NumCasts > 0 ? master.LastFrameMovement.Normalized() : default;
var capsule = ShapeDistance.Capsule(master.Position + 2 * moveDir, moveDir, 4, 1);
var capsule = ShapeDistance.Capsule(master.Position + 2 * moveDir, moveDir, 4, 1.5f);
hints.AddForbiddenZone(p => -capsule(p), DateTime.MaxValue);
}

Expand Down
12 changes: 6 additions & 6 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P2MirrorMirror.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,21 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
{
0 => -135.Degrees(),
1 => 135.Degrees(),
2 => -90.Degrees(),
3 => 90.Degrees(),
2 => -95.Degrees(),
3 => 95.Degrees(),
_ => default
};
}
else
{
dir = Angle.FromDirection(origin.Actor.Position - Arena.Center) + group switch
{
0 => (RedRangedLeftOfMelee ? -90 : 90).Degrees(),
1 => (RedRangedLeftOfMelee ? 90 : -90).Degrees(),
0 => (RedRangedLeftOfMelee ? -95 : 95).Degrees(),
1 => (RedRangedLeftOfMelee ? 95 : -95).Degrees(),
2 => 180.Degrees(),
3 => (RedRangedLeftOfMelee ? -135 : 135).Degrees(),
4 => -90.Degrees(),
5 => 90.Degrees(),
4 => -95.Degrees(),
5 => 95.Degrees(),
6 => (RedRangedLeftOfMelee ? 180 : -135).Degrees(),
7 => (RedRangedLeftOfMelee ? 135 : 180).Degrees(),
_ => default
Expand Down
7 changes: 4 additions & 3 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P3Apocalypse.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.ComponentModel;

namespace BossMod.Dawntrail.Ultimate.FRU;
namespace BossMod.Dawntrail.Ultimate.FRU;

class P3Apocalypse(BossModule module) : Components.GenericAOEs(module)
{
Expand Down Expand Up @@ -268,7 +266,10 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
{
var safeSpot = SafeOffset(slot, out _);
if (safeSpot != default)
{
hints.PathfindMapBounds = FRU.PathfindHugBorderBounds;
hints.AddForbiddenZone(ShapeDistance.PrecisePosition(Arena.Center + safeSpot, new(0, 1), Arena.Bounds.MapResolution, actor.Position, 0.1f), Spreads.Count != 0 ? Spreads[0].Activation : DateTime.MaxValue);
}
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
Expand Down
7 changes: 4 additions & 3 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -399,10 +399,10 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
if (hint.offset != default)
{
// we want to stay really close to border
if (hint.offset.LengthSq() > 18 * 18)
hint.offset *= 19.5f / 19;
if (hint.offset.LengthSq() > 324)
hint.offset *= 1.02632f;

if (hint.hint.HasFlag(Hint.KnockbackFrom) && Raid.WithoutSlot().Any(p => p.PendingKnockbacks.Count > 0))
if (hint.hint.HasFlag(Hint.KnockbackFrom) && Raid.WithoutSlot(false, true, true).Any(p => p.PendingKnockbacks.Count > 0))
{
return; // don't even try moving until all knockbacks are resolved, that can fuck up others...
}
Expand All @@ -412,6 +412,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
}
if (hint.hint.HasFlag(Hint.SafespotPrecise))
{
hints.PathfindMapBounds = FRU.PathfindHugBorderBounds;
hints.AddForbiddenZone(ShapeDistance.PrecisePosition(Arena.Center + hint.offset, new(0, 1), Arena.Bounds.MapResolution, actor.Position, 0.1f));
}
if (hint.hint.HasFlag(Hint.Maelstrom) && _hourglass != null)
Expand Down
15 changes: 15 additions & 0 deletions BossMod/Util/Intersect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,19 @@ public static bool CircleCone(WDir circleOffset, float circleRadius, float coneR
return (circleOffset - corner).LengthSq() <= rsq;
}
public static bool CircleCone(WPos circleCenter, float circleRadius, WPos coneCenter, float coneRadius, WDir coneDir, Angle halfAngle) => CircleCone(circleCenter - coneCenter, circleRadius, coneRadius, coneDir, halfAngle);

public static bool CircleAARect(WDir circleOffset, float circleRadius, float halfExtentX, float halfExtentZ)
{
circleOffset = circleOffset.Abs(); // symmetrical along X/Z, consider only positive quadrant
var cornerOffset = circleOffset - new WDir(halfExtentX, halfExtentZ); // relative to corner
if (cornerOffset.X > circleRadius || cornerOffset.Z > circleRadius)
return false; // circle is too far from one of the edges, so can't intersect
if (cornerOffset.X <= 0 || cornerOffset.Z <= 0)
return true; // circle center is inside/on the edge, or close enough to one of the edges to intersect
return cornerOffset.LengthSq() <= circleRadius * circleRadius; // check whether circle touches the corner
}
public static bool CircleAARect(WPos circleCenter, float circleRadius, WPos rectCenter, float halfExtentX, float halfExtentZ) => CircleAARect(circleCenter - rectCenter, circleRadius, halfExtentX, halfExtentZ);

public static bool CircleRect(WDir circleOffset, float circleRadius, WDir rectZDir, float halfExtentX, float halfExtentZ) => CircleAARect(circleOffset.Rotate(rectZDir.MirrorX()), circleRadius, halfExtentX, halfExtentZ);
public static bool CircleRect(WPos circleCenter, float circleRadius, WPos rectCenter, WDir rectZDir, float halfExtentX, float halfExtentZ) => CircleRect(circleCenter - rectCenter, circleRadius, rectZDir, halfExtentX, halfExtentZ);
}
2 changes: 2 additions & 0 deletions BossMod/Util/WPosDir.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public WDir(Vector2 v) : this(v.X, v.Y) { }
public readonly WDir Sign() => new(Math.Sign(X), Math.Sign(Z));
public readonly WDir OrthoL() => new(Z, -X); // CCW, same length
public readonly WDir OrthoR() => new(-Z, X); // CW, same length
public readonly WDir MirrorX() => new(-X, Z);
public readonly WDir MirrorZ() => new(X, -Z);
public static float Dot(WDir a, WDir b) => a.X * b.X + a.Z * b.Z;
public readonly float Dot(WDir a) => X * a.X + Z * a.Z;
public static float Cross(WDir a, WDir b) => a.X * b.Z - a.Z * b.X;
Expand Down
2 changes: 0 additions & 2 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
immediate plans
- circle vs rect/cone intersection
- proper circle bounds -> revise everything in fru
- ai refactoring
-- high-level ai modules
--- ordered before standard rotation modules
Expand Down
34 changes: 25 additions & 9 deletions UIDev/IntersectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ class IntersectionTest : TestWindow
private float _cirleOffset = 5.5f;
private float _circleDir = 0;
private float _circleRadius = 0.5f;
private float _coneRadius = 5;
private float _coneHalfAngleDeg = 30;
private float _coneDirDeg = 90;
private float _shapeExtentPrimary = 5; // z/r
private float _shapeExtentSecondary = 30; // x/phi (deg)
private float _shapeDirDeg = 90;
private bool _shapeIsRect;
private readonly MiniArena _arena = new(new(), default, new ArenaBoundsSquare(10));

public IntersectionTest() : base("Intersection test", new(400, 400), ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
Expand All @@ -22,16 +23,31 @@ public override void Draw()
ImGui.DragFloat("Circle offset", ref _cirleOffset, 0.1f, 0, 10);
ImGui.DragFloat("Circle dir", ref _circleDir, 1, -180, 180);
ImGui.DragFloat("Circle radius", ref _circleRadius, 0.1f, 0.1f, 5);
ImGui.DragFloat("Cone radius", ref _coneRadius, 0.1f, 0.1f, 10);
ImGui.DragFloat("Cone half-angle", ref _coneHalfAngleDeg, 1, 0, 180);
ImGui.DragFloat("Cone dir", ref _coneDirDeg, 1, -180, 180);
ImGui.Checkbox("Intersect with rect", ref _shapeIsRect);
if (_shapeIsRect)
{
ImGui.DragFloat("Rect half-Z", ref _shapeExtentPrimary, 0.1f, 0.1f, 10);
ImGui.DragFloat("Rect half-X", ref _shapeExtentSecondary, 0.1f, 0.1f, 30);
ImGui.DragFloat("Rect dir", ref _shapeDirDeg, 1, -180, 180);
}
else
{
ImGui.DragFloat("Cone radius", ref _shapeExtentPrimary, 0.1f, 0.1f, 10);
ImGui.DragFloat("Cone half-angle", ref _shapeExtentSecondary, 1, 0, 180);
ImGui.DragFloat("Cone dir", ref _shapeDirDeg, 1, -180, 180);
}
var circleCenter = _cirleOffset * _circleDir.Degrees().ToDirection();
var intersect = Intersect.CircleCone(circleCenter, _circleRadius, _coneRadius, _coneDirDeg.Degrees().ToDirection(), _coneHalfAngleDeg.Degrees());
var intersect = _shapeIsRect
? Intersect.CircleRect(circleCenter, _circleRadius, _shapeDirDeg.Degrees().ToDirection(), _shapeExtentSecondary, _shapeExtentPrimary)
: Intersect.CircleCone(circleCenter, _circleRadius, _shapeExtentPrimary, _shapeDirDeg.Degrees().ToDirection(), _shapeExtentSecondary.Degrees());
ImGui.TextUnformatted($"Intersect: {intersect}");

_arena.Begin(default);
_arena.AddCone(default, _coneRadius, _coneDirDeg.Degrees(), _coneHalfAngleDeg.Degrees(), Colors.Safe);
if (_shapeIsRect)
_arena.AddRect(default, _shapeDirDeg.Degrees().ToDirection(), _shapeExtentPrimary, _shapeExtentPrimary, _shapeExtentSecondary, Colors.Safe);
else
_arena.AddCone(default, _shapeExtentPrimary, _shapeDirDeg.Degrees(), _shapeExtentSecondary.Degrees(), Colors.Safe);
_arena.AddCircle(circleCenter.ToWPos(), _circleRadius, Colors.Danger);
MiniArena.End();
_arena.End();
}
}

0 comments on commit 7b56453

Please sign in to comment.