diff --git a/BossMod/BossModule/AIHintsVisualizer.cs b/BossMod/BossModule/AIHintsVisualizer.cs index d75b7d1b71..908b751ac3 100644 --- a/BossMod/BossModule/AIHintsVisualizer.cs +++ b/BossMod/BossModule/AIHintsVisualizer.cs @@ -49,7 +49,7 @@ public void Draw(UITree tree) private MapVisualizer BuildZoneVisualizer(Func shape) { - var map = hints.Bounds.PathfindMap(); + var map = hints.Bounds.PathfindMap(hints.Center); map.BlockPixelsInside(shape, 0, NavigationDecision.DefaultForbiddenZoneCushion); return new MapVisualizer(map, 0, player.Position); } @@ -61,7 +61,7 @@ private MapVisualizer BuildPathfindingVisualizer() _navi = BuildPathfind(targeting.enemy, targeting.range, targeting.pos, targeting.tank); if (_navi.Map == null) { - _navi.Map = hints.Bounds.PathfindMap(); + _navi.Map = hints.Bounds.PathfindMap(hints.Center); var imm = NavigationDecision.ImminentExplosionTime(ws.CurrentTime); foreach (var (shape, activation) in hints.ForbiddenZones) NavigationDecision.AddBlockerZone(_navi.Map, imm, activation, shape, NavigationDecision.DefaultForbiddenZoneCushion); diff --git a/BossMod/BossModule/ArenaBounds.cs b/BossMod/BossModule/ArenaBounds.cs index 375e827615..beadee7c85 100644 --- a/BossMod/BossModule/ArenaBounds.cs +++ b/BossMod/BossModule/ArenaBounds.cs @@ -31,7 +31,7 @@ public float ScreenHalfSize } protected abstract PolygonClipper.Operand BuildClipPoly(); - public abstract Pathfinding.Map PathfindMap(); // if implementation caches a map, it should return a clone + public abstract Pathfinding.Map PathfindMap(WPos center); // if implementation caches a map, it should return a clone public abstract bool Contains(WDir offset); public abstract float IntersectRay(WDir originOffset, WDir dir); public abstract WDir ClampToBounds(WDir offset); @@ -103,7 +103,7 @@ public record class ArenaBoundsCircle(WPos Center, float Radius, float MapResolu private Pathfinding.Map? _cachedMap; protected override PolygonClipper.Operand BuildClipPoly() => new(CurveApprox.Circle(Radius, MaxApproxError)); - public override Pathfinding.Map PathfindMap() => (_cachedMap ??= BuildMap()).Clone(); + public override Pathfinding.Map PathfindMap(WPos center) => (_cachedMap ??= BuildMap()).Clone(center); public override bool Contains(WDir offset) => offset.LengthSq() <= Radius * Radius; public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayCircle(originOffset, dir, Radius); @@ -116,8 +116,8 @@ public override WDir ClampToBounds(WDir offset) private Pathfinding.Map BuildMap() { - var map = new Pathfinding.Map(MapResolution, Center, Radius, Radius); - map.BlockPixelsInside(ShapeDistance.InvertedCircle(Center, Radius), 0, 0); + var map = new Pathfinding.Map(MapResolution, default, Radius, Radius); + map.BlockPixelsInside(ShapeDistance.InvertedCircle(default, Radius), 0, 0); return map; } } @@ -127,7 +127,7 @@ public record class ArenaBoundsSquare(WPos Center, float Radius, float MapResolu public float HalfWidth => Radius; protected override PolygonClipper.Operand BuildClipPoly() => new(CurveApprox.Rect(new(Radius, 0), new(0, Radius))); - public override Pathfinding.Map PathfindMap() => new(MapResolution, Center, Radius, Radius); + public override Pathfinding.Map PathfindMap(WPos center) => new(MapResolution, center, Radius, Radius); public override bool Contains(WDir offset) => offset.AlmostZero(Radius); public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayAABB(originOffset, dir, Radius, Radius); @@ -147,7 +147,7 @@ public record class ArenaBoundsRect(WPos Center, float HalfWidth, float HalfHeig private WDir _orientation = Rotation.ToDirection(); protected override PolygonClipper.Operand BuildClipPoly() => new(CurveApprox.Rect(_orientation, HalfWidth, HalfHeight)); - public override Pathfinding.Map PathfindMap() => new(MapResolution, Center, HalfWidth, HalfHeight, Rotation); + public override Pathfinding.Map PathfindMap(WPos center) => new(MapResolution, center, HalfWidth, HalfHeight, Rotation); public override bool Contains(WDir offset) => offset.InRect(_orientation, HalfHeight, HalfHeight, HalfWidth); public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayRect(originOffset, dir, _orientation, HalfWidth, HalfHeight); @@ -169,7 +169,7 @@ public record class ArenaBoundsCustom(WPos Center, float Radius, RelSimplifiedCo private Pathfinding.Map? _cachedMap; protected override PolygonClipper.Operand BuildClipPoly() => new(Poly); - public override Pathfinding.Map PathfindMap() => (_cachedMap ??= BuildMap()).Clone(); + public override Pathfinding.Map PathfindMap(WPos center) => (_cachedMap ??= BuildMap()).Clone(center); public override bool Contains(WDir offset) => Poly.Contains(offset); public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayPolygon(originOffset, dir, Poly); public override WDir ClampToBounds(WDir offset) @@ -182,7 +182,7 @@ public override WDir ClampToBounds(WDir offset) private Pathfinding.Map BuildMap() { - var map = new Pathfinding.Map(MapResolution, Center, Radius, Radius); + var map = new Pathfinding.Map(MapResolution, default, Radius, Radius); var tri = ShapeDistance.TriList(default, Poly.Triangulate()); map.BlockPixelsInside(p => -tri(p), 0, 0); return map; diff --git a/BossMod/Pathfinding/Map.cs b/BossMod/Pathfinding/Map.cs index 222d283e73..e91e4f1520 100644 --- a/BossMod/Pathfinding/Map.cs +++ b/BossMod/Pathfinding/Map.cs @@ -21,7 +21,7 @@ public struct Pixel public int Height { get; private init; } // always even public Pixel[] Pixels; - public WPos Center { get; set; } // position of map center in world units + public WPos Center { get; private set; } // position of map center in world units public Angle Rotation { get; private init; } // rotation relative to world space (=> ToDirection() is equal to direction of local 'height' axis in world space) private WDir LocalZDivRes { get; init; } @@ -44,11 +44,12 @@ public struct Pixel LocalZDivRes = rotation.ToDirection() / Resolution; } - public Map Clone() + public Map Clone(WPos center) { var res = (Map)MemberwiseClone(); res.Pixels = new Pixel[Pixels.Length]; Array.Copy(Pixels, res.Pixels, Pixels.Length); + res.Center = center; return res; } diff --git a/BossMod/Pathfinding/NavigationDecision.cs b/BossMod/Pathfinding/NavigationDecision.cs index 1a5ce12b92..16faacee37 100644 --- a/BossMod/Pathfinding/NavigationDecision.cs +++ b/BossMod/Pathfinding/NavigationDecision.cs @@ -49,7 +49,7 @@ public static NavigationDecision Build(WorldState ws, AIHints hints, Actor playe // we're in forbidden zone => find path to safety (and ideally to uptime zone) // if such a path can't be found (that's always the case if we're inside imminent forbidden zone, but can also happen in other cases), try instead to find a path to safety that doesn't enter any other zones that we're not inside // first build a map with zones that we're outside of as blockers - var map = hints.Bounds.PathfindMap(); + var map = hints.Bounds.PathfindMap(hints.Center); foreach (var (zf, inside) in hints.ForbiddenZones.Zip(inZone)) if (!inside) AddBlockerZone(map, imminent, zf.activation, zf.shapeDistance, forbiddenZoneCushion); @@ -57,7 +57,7 @@ public static NavigationDecision Build(WorldState ws, AIHints hints, Actor playe bool inImminentForbiddenZone = inZone.Take(numImminentZones).Any(inside => inside); if (!inImminentForbiddenZone) { - var map2 = map.Clone(); + var map2 = map.Clone(map.Center); foreach (var (zf, inside) in hints.ForbiddenZones.Zip(inZone)) if (inside) AddBlockerZone(map2, imminent, zf.activation, zf.shapeDistance, forbiddenZoneCushion); @@ -88,7 +88,7 @@ public static NavigationDecision Build(WorldState ws, AIHints hints, Actor playe if (!player.Position.InCircle(targetPos.Value, targetRadius)) { // we're not in uptime zone, just run to it, avoiding any aoes - var map = hints.Bounds.PathfindMap(); + var map = hints.Bounds.PathfindMap(hints.Center); foreach (var (shape, activation) in hints.ForbiddenZones) AddBlockerZone(map, imminent, activation, shape, forbiddenZoneCushion); int maxGoal = AddTargetGoal(map, targetPos.Value, targetRadius, targetRot, Positional.Any, 0); @@ -130,7 +130,7 @@ public static NavigationDecision Build(WorldState ws, AIHints hints, Actor playe if (!inPositional) { // we're in uptime zone, but not in correct quadrant - move there, avoiding all aoes and staying within uptime zone - var map = hints.Bounds.PathfindMap(); + var map = hints.Bounds.PathfindMap(hints.Center); map.BlockPixelsInside(ShapeDistance.InvertedCircle(targetPos.Value, targetRadius), 0, 0); foreach (var (shape, activation) in hints.ForbiddenZones) AddBlockerZone(map, imminent, activation, shape, forbiddenZoneCushion);