Skip to content

Commit

Permalink
masked carnivale stage 32 act 2 improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
CarnifexOptimus committed Dec 10, 2024
1 parent dba72cb commit 533ab61
Show file tree
Hide file tree
Showing 24 changed files with 237 additions and 114 deletions.
2 changes: 1 addition & 1 deletion BossMod/Autorotation/UIRotationWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public override void Draw()
if (newSel >= 0 && _mgr.Preset != null)
{
ImGui.SameLine();
using var style = ImRaii.PushColor(ImGuiCol.Text, 0xff00ffff);
using var style = ImRaii.PushColor(ImGuiCol.Text, Colors.TextColor2);
UIMisc.HelpMarker(() => "You have a preset activated, which fully overrides the CD plan!", FontAwesomeIcon.ExclamationTriangle);
}
}
Expand Down
103 changes: 67 additions & 36 deletions BossMod/BossModule/AOEShapes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,53 +192,86 @@ public enum OperandType
// if the origin of the AOE can change, edit the origin default value to prevent cache issues
public sealed record class AOEShapeCustom(IEnumerable<Shape> Shapes1, IEnumerable<Shape>? DifferenceShapes = null, IEnumerable<Shape>? Shapes2 = null, bool InvertForbiddenZone = false, OperandType Operand = OperandType.Union, WPos Origin = default) : AOEShape
{
private RelSimplifiedComplexPolygon? polygon;
public RelSimplifiedComplexPolygon? Polygon;
private PolygonWithHolesDistanceFunction? shapeDistance;
private readonly int hashkey = CreateCacheKey(Shapes1, Shapes2 ?? [], DifferenceShapes ?? [], Operand, Origin);
public static readonly Dictionary<int, RelSimplifiedComplexPolygon> Cache = [];
public static readonly LinkedList<int> CacheOrder = new();
private static readonly Dictionary<int, RelSimplifiedComplexPolygon> cache = [];
private static readonly LinkedList<int> cacheOrder = new();
public void AddToCache(RelSimplifiedComplexPolygon value)
{
if (Cache.Count >= 50)
if (cache.Count >= 50)
{
var lruKey = CacheOrder.Last?.Value;
var lruKey = cacheOrder.Last?.Value;
if (lruKey != null)
{
Cache.Remove(lruKey.Value);
CacheOrder.RemoveLast();
cache.Remove(lruKey.Value);
cacheOrder.RemoveLast();
}
}
Cache[hashkey] = value;
CacheOrder.Remove(hashkey);
CacheOrder.AddFirst(hashkey);
cache[hashkey] = value;
cacheOrder.Remove(hashkey);
cacheOrder.AddFirst(hashkey);
}

public override string ToString() => $"Custom AOE shape: hashkey={hashkey}, ifz={InvertForbiddenZone}";

private RelSimplifiedComplexPolygon GetCombinedPolygon(WPos origin)
public RelSimplifiedComplexPolygon GetCombinedPolygon(WPos origin)
{
if (Cache.TryGetValue(hashkey, out var cachedResult)) // for moving custom AOEs we don't want to recalculate the polygon every frame since they move at server ticks and not frame
if (cache.TryGetValue(hashkey, out var cachedResult))
{
CacheOrder.Remove(hashkey);
CacheOrder.AddFirst(hashkey);
return polygon = cachedResult;
cacheOrder.Remove(hashkey);
cacheOrder.AddFirst(hashkey);
return Polygon = cachedResult;
}

var shapes1 = CreateOperandFromShapes(Shapes1, origin);
var shapes2 = CreateOperandFromShapes(Shapes2, origin);
var differenceOperands = CreateOperandFromShapes(DifferenceShapes, origin);

var clipper = new PolygonClipper();
var combinedShapes = Operand switch
if (Shapes2 == null)
{
OperandType.Xor => clipper.Xor(shapes1, shapes2),
OperandType.Intersection => clipper.Intersect(shapes1, shapes2),
_ => null
};

polygon = combinedShapes != null
? clipper.Difference(new PolygonClipper.Operand(combinedShapes), differenceOperands)
: clipper.Difference(shapes1, differenceOperands);
AddToCache(polygon);
return polygon;
if (DifferenceShapes != null)
{
Polygon = clipper.Difference(shapes1, differenceOperands);
AddToCache(Polygon);
return Polygon;
}
else
{
Polygon = clipper.Simplify(shapes1);
AddToCache(Polygon);
return Polygon;
}
}
if (Shapes2 != null)
{
Polygon = clipper.Simplify(shapes1);
foreach (var shape in Shapes2)
{
var singleShapeOperand = CreateOperandFromShape(shape, origin);

switch (Operand)
{
case OperandType.Intersection:
Polygon = clipper.Intersect(new PolygonClipper.Operand(Polygon), singleShapeOperand);
break;
case OperandType.Xor:
Polygon = clipper.Xor(new PolygonClipper.Operand(Polygon), singleShapeOperand);
break;
}
}
Polygon = DifferenceShapes != null ? clipper.Difference(new PolygonClipper.Operand(Polygon), differenceOperands) : Polygon;
AddToCache(Polygon);
return Polygon;
}
return new();
}

private static PolygonClipper.Operand CreateOperandFromShape(Shape shape, WPos origin)
{
var operand = new PolygonClipper.Operand();
operand.AddPolygon(shape.ToPolygon(origin));
return operand;
}

private static PolygonClipper.Operand CreateOperandFromShapes(IEnumerable<Shape>? shapes, WPos origin)
Expand All @@ -252,8 +285,8 @@ private static PolygonClipper.Operand CreateOperandFromShapes(IEnumerable<Shape>

public override bool Check(WPos position, WPos origin, Angle rotation)
{
var relativePosition = position - origin;
var result = (polygon ?? GetCombinedPolygon(origin)).Contains(new(relativePosition.X, relativePosition.Z));
var (x, z) = position - origin;
var result = (Polygon ?? GetCombinedPolygon(origin)).Contains(new(x, z));
return result;
}

Expand All @@ -269,12 +302,12 @@ private static int CreateCacheKey(IEnumerable<Shape> shapes1, IEnumerable<Shape>

public override void Draw(MiniArena arena, WPos origin, Angle rotation, uint color = 0)
{
arena.ZoneRelPoly(hashkey, polygon ?? GetCombinedPolygon(origin), color);
arena.ZoneRelPoly(hashkey, Polygon ?? GetCombinedPolygon(origin), color);
}

public override void Outline(MiniArena arena, WPos origin, Angle rotation, uint color = 0)
{
var combinedPolygon = polygon ?? GetCombinedPolygon(origin);
var combinedPolygon = Polygon ?? GetCombinedPolygon(origin);
for (var i = 0; i < combinedPolygon.Parts.Count; ++i)
{
var part = combinedPolygon.Parts[i];
Expand Down Expand Up @@ -305,23 +338,21 @@ public override void Outline(MiniArena arena, WPos origin, Angle rotation, uint

public override Func<WPos, float> Distance(WPos origin, Angle rotation)
{
shapeDistance ??= new PolygonWithHolesDistanceFunction(origin, polygon ?? GetCombinedPolygon(origin));
shapeDistance ??= new PolygonWithHolesDistanceFunction(origin, Polygon ?? GetCombinedPolygon(origin));
var dis = shapeDistance.Value.Distance;
return InvertForbiddenZone ? p => -dis(p) : dis;
}
}

public sealed record class AOEShapeCustomAlt(RelSimplifiedComplexPolygon Poly, Angle DirectionOffset = default, bool InvertForbiddenZone = false) : AOEShape
{
private PolygonWithHolesDistanceFunction? shapeDistance;
public override string ToString() => $"Custom: off={DirectionOffset}, ifz={InvertForbiddenZone}";
public override bool Check(WPos position, WPos origin, Angle rotation) => Poly.Contains((position - origin).Rotate(-rotation - DirectionOffset));
public override void Draw(MiniArena arena, WPos origin, Angle rotation, uint color = 0) => arena.ZoneComplex(origin, rotation + DirectionOffset, Poly, color);
public override void Outline(MiniArena arena, WPos origin, Angle rotation, uint color = 0) => arena.AddComplexPolygon(origin, (rotation + DirectionOffset).ToDirection(), Poly, color);
public override Func<WPos, float> Distance(WPos origin, Angle rotation)
{
shapeDistance ??= new PolygonWithHolesDistanceFunction(origin, Poly);
var dis = shapeDistance.Value.Distance;
return InvertForbiddenZone ? p => -dis(p) : dis;
var shapeDistance = new PolygonWithHolesDistanceFunction(origin, Poly.Transform(default, (-rotation - DirectionOffset).ToDirection())).Distance;
return InvertForbiddenZone ? p => -shapeDistance(p) : shapeDistance;
}
}
8 changes: 3 additions & 5 deletions BossMod/BossModule/ArenaBounds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public override WDir ClampToBounds(WDir offset)
private Pathfinding.Map BuildMap()
{
var map = new Pathfinding.Map(MapResolution, default, Radius, Radius);
map.BlockPixelsInsideConvex(p => -ShapeDistance.Circle(default, Radius)(p), float.NegativeInfinity, 0);
map.BlockPixelsInsideConvex(p => -ShapeDistance.Circle(default, Radius)(p), -1, 0);
return map;
}
}
Expand All @@ -149,7 +149,7 @@ private static float CalculateScaleFactor(Angle Rotation)
private Pathfinding.Map BuildMap()
{
var map = new Pathfinding.Map(MapResolution, default, HalfWidth, HalfHeight, Rotation);
map.BlockPixelsInsideConvex(p => -ShapeDistance.Rect(default, Rotation, HalfHeight, HalfHeight, HalfWidth)(p), float.NegativeInfinity, 0);
map.BlockPixelsInsideConvex(p => -ShapeDistance.Rect(default, Rotation, HalfHeight, HalfHeight, HalfWidth)(p), -1, 0);
return map;
}

Expand All @@ -158,8 +158,6 @@ private Pathfinding.Map BuildMap()

public override WDir ClampToBounds(WDir offset)
{
if (offset.X == default) // if actor is almost in the center of the arena, do nothing
return offset;
var offsetX = offset.Dot(Orientation.OrthoL());
var offsetY = offset.Dot(Orientation);
if (Math.Abs(offsetX) > HalfWidth)
Expand Down Expand Up @@ -325,7 +323,7 @@ private Pathfinding.Map BuildMap()
}

ref var pixel = ref pixels[rowOffset + x];
pixel.MaxG = allInside ? float.MaxValue : float.NegativeInfinity;
pixel.MaxG = allInside ? float.MaxValue : -1;
}
});

Expand Down
24 changes: 11 additions & 13 deletions BossMod/BossModule/Shapes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,10 @@ public override List<WDir> Contour(WPos center)
var angleIncrement = Angle.DoublePI / Edges;
var initialRotation = Rotation.Rad;
var vertices = new List<WDir>(Edges);
for (var i = Edges - 1; i >= 0; --i)
for (var i = 0; i < Edges; ++i)
{
var (sin, cos) = ((float, float))Math.SinCos(i * angleIncrement + initialRotation);
vertices.Add(new(Center.X + Radius * cos, Center.Z + Radius * sin));
vertices.Add(new(Center.X + Radius * sin, Center.Z + Radius * cos));
}
Points = [.. vertices];
}
Expand Down Expand Up @@ -303,7 +303,7 @@ public override List<WDir> Contour(WPos center)
public sealed record class DonutSegmentHA(WPos Center, float InnerRadius, float OuterRadius, Angle CenterDir, Angle HalfAngle) : DonutSegment(Center, InnerRadius, OuterRadius,
CenterDir - HalfAngle, CenterDir + HalfAngle);

// Approximates a cone with a customizable number of edges for the circle arc
// Approximates a cone with a customizable number of edges for the circle arc - with 1 edge this turns into a triangle, 2 edges result in a parallelogram
public sealed record class ConeV(WPos Center, float Radius, Angle CenterDir, Angle HalfAngle, int Edges) : Shape
{
public override List<WDir> Contour(WPos center)
Expand All @@ -313,11 +313,10 @@ public override List<WDir> Contour(WPos center)
var angleIncrement = 2 * HalfAngle.Rad / Edges;
var startAngle = CenterDir.Rad - HalfAngle.Rad;
var vertices = new List<WDir>(Edges + 1);

for (var i = 0; i < Edges + 1; ++i)
{
var (sin, cos) = ((float, float))Math.SinCos(startAngle + i * angleIncrement);
vertices.Add(new(Center.X + Radius * cos, Center.Z + Radius * sin));
vertices.Add(new(Center.X + Radius * sin, Center.Z + Radius * cos));
}
vertices.Add(Center - new WPos());
Points = [.. vertices];
Expand All @@ -342,17 +341,15 @@ public override List<WDir> Contour(WPos center)
var angleIncrement = 2 * HalfAngle.Rad / Edges;
var startAngle = CenterDir.Rad - HalfAngle.Rad;
var vertices = new List<WDir>(2 * (Edges + 1));

for (var i = Edges; i >= 0; --i)
for (var i = 0; i < Edges + 1; ++i)
{
var (sin, cos) = ((float, float))Math.SinCos(startAngle + i * angleIncrement);
vertices.Add(new(Center.X + OuterRadius * cos, Center.Z + OuterRadius * sin));
vertices.Add(new(Center.X + OuterRadius * sin, Center.Z + OuterRadius * cos));
}

for (var i = 0; i < Edges + 1; ++i)
for (var i = Edges; i >= 0; --i)
{
var (sin, cos) = ((float, float))Math.SinCos(startAngle + i * angleIncrement);
vertices.Add(new(Center.X + InnerRadius * cos, Center.Z + InnerRadius * sin));
vertices.Add(new(Center.X + InnerRadius * sin, Center.Z + InnerRadius * cos));
}
Points = [.. vertices];
}
Expand All @@ -366,6 +363,7 @@ public override List<WDir> Contour(WPos center)
public override string ToString() => $"DonutSegmentV:{Center.X},{Center.Z},{InnerRadius},{OuterRadius},{CenterDir},{HalfAngle},{Edges}";
}

// Approximates a donut with a customizable number of edges per circle arc
public sealed record class DonutV(WPos Center, float InnerRadius, float OuterRadius, int Edges) : Shape
{
public override List<WDir> Contour(WPos center)
Expand All @@ -378,13 +376,13 @@ public override List<WDir> Contour(WPos center)
for (var i = 0; i <= Edges; ++i)
{
var (sin, cos) = ((float, float))Math.SinCos(i * angleIncrement);
vertices.Add(new(Center.X + OuterRadius * cos, Center.Z + OuterRadius * sin));
vertices.Add(new(Center.X + OuterRadius * sin, Center.Z + OuterRadius * cos));
}

for (var i = Edges; i >= 0; --i)
{
var (sin, cos) = ((float, float))Math.SinCos(i * angleIncrement);
vertices.Add(new(Center.X + InnerRadius * cos, Center.Z + InnerRadius * sin));
vertices.Add(new(Center.X + InnerRadius * sin, Center.Z + InnerRadius * cos));
}
Points = [.. vertices];
}
Expand Down
19 changes: 18 additions & 1 deletion BossMod/Components/Gaze.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public override void DrawArenaForeground(int pcSlot, Actor pc)
foreach (var eye in ActiveEyes(pcSlot, pc))
{
var danger = HitByEye(pc, eye) != Inverted;
var eyeCenter = Arena.WorldPositionToScreenPosition(eye.Position);
var eyeCenter = IndicatorScreenPos(eye.Position);
DrawEye(eyeCenter, danger);

if (pc.Position.InCircle(eye.Position, eye.Range))
Expand All @@ -71,6 +71,23 @@ public static void DrawEye(Vector2 eyeCenter, bool danger)
}

private static bool HitByEye(Actor actor, Eye eye) => (actor.Rotation + eye.Forward).ToDirection().Dot((eye.Position - actor.Position).Normalized()) >= 0.707107f; // 45-degree

private Vector2 IndicatorScreenPos(WPos eye)
{
if (Arena.InBounds(eye) || Arena.Bounds is not ArenaBoundsCircle and not ArenaBoundsSquare)
{
return Arena.WorldPositionToScreenPosition(eye);
}
else if (Arena.Bounds is ArenaBoundsRect)
{
return Arena.WorldPositionToScreenPosition(Arena.ClampToBounds(eye) + 2 * (eye - Arena.Center).Normalized());
}
else
{
var dir = (eye - Arena.Center).Normalized();
return Arena.ScreenCenter + Arena.RotatedCoords(dir.ToVec2()) * (Arena.ScreenHalfSize + Arena.ScreenMarginSize * 0.5f);
}
}
}

// gaze that happens on cast end
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Config/ColorConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public sealed class ColorConfig : ConfigNode
public Color ArenaMeleeRangeIndicator = new(0xffff0000);

[PropertyDisplay("Arena: other")]
public Color[] ArenaOther = [new(0xffff0080), new(0xff8080ff), new(0xff80ff80), new(0xffff8040), new(0xff40c0c0), new(0x40008080)];
public Color[] ArenaOther = [new(0xffff0080), new(0xff8080ff), new(0xff80ff80), new(0xffff8040), new(0xff40c0c0), new(0x40008080), new(0xffffff00), new(0xffff8000)];

[PropertyDisplay("Arena: interesting player, important for a mechanic")]
public Color ArenaPlayerInteresting = new(0xffc0c0c0);
Expand Down
12 changes: 6 additions & 6 deletions BossMod/Debug/DebugObstacles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ protected override void DrawSidebar()
var py = (int)playerOffset.Z;
var playerDeepInObstacle = px >= 0 && py >= 0 && px < Bitmap.Width && py < Bitmap.Height && Bitmap[px, py]
&& (px == 0 || Bitmap[px - 1, py]) && (py == 0 || Bitmap[px, py - 1]) && (px == (Bitmap.Width - 1) || Bitmap[px + 1, py]) && (py == (Bitmap.Height - 1) || Bitmap[px, py + 1]);
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xff0000ff, playerDeepInObstacle);
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.TextColor3, playerDeepInObstacle);
ImGui.TextUnformatted($"Player cell: {px}x{py}");
if (playerDeepInObstacle)
{
Expand All @@ -110,10 +110,10 @@ protected override void DrawSidebar()
var y = player?.PosRot.Y ?? 0;
var tl = e.Origin + new WDir(HoveredPixel.x, HoveredPixel.y) * Bitmap.PixelSize;
var br = tl + new WDir(Bitmap.PixelSize, Bitmap.PixelSize);
Camera.Instance?.DrawWorldLine(new(tl.X, y, tl.Z), new(tl.X, y, br.Z), 0xff00ffff);
Camera.Instance?.DrawWorldLine(new(tl.X, y, br.Z), new(br.X, y, br.Z), 0xff00ffff);
Camera.Instance?.DrawWorldLine(new(br.X, y, br.Z), new(br.X, y, tl.Z), 0xff00ffff);
Camera.Instance?.DrawWorldLine(new(br.X, y, tl.Z), new(tl.X, y, tl.Z), 0xff00ffff);
Camera.Instance?.DrawWorldLine(new(tl.X, y, tl.Z), new(tl.X, y, br.Z), Colors.TextColor2);
Camera.Instance?.DrawWorldLine(new(tl.X, y, br.Z), new(br.X, y, br.Z), Colors.TextColor2);
Camera.Instance?.DrawWorldLine(new(br.X, y, br.Z), new(br.X, y, tl.Z), Colors.TextColor2);
Camera.Instance?.DrawWorldLine(new(br.X, y, tl.Z), new(tl.X, y, tl.Z), Colors.TextColor2);
}
}

Expand All @@ -123,7 +123,7 @@ protected override void DrawSidebar()
if (player != null)
{
var playerOffset = ((player.Position - e.Origin) / Bitmap.PixelSize).Floor();
yield return ((int)playerOffset.X, (int)playerOffset.Z, new(0xff00ff00));
yield return ((int)playerOffset.X, (int)playerOffset.Z, new(Colors.Safe));
}
}
}
Expand Down
Loading

0 comments on commit 533ab61

Please sign in to comment.