Fix TilingPath bugs affecting unofficial mods
TilingPath had some bugs when working with single-point segments, which exist outside of the official mods. This change fixes a few problems: - Avoid unbounded length zero-cost, zero-progression loops that could be formed by an arbitrary number of single-point corners. These could pop up arbitrarily in the middle of paths. - Avoid zero-progression loops short-circuiting looped paths. - Fix missing end type matching during back-tracing stage. - Enhance lint check to ensure starts/ends have a valid directions. - Remove some dead code.
This commit is contained in:
committed by
Gustas Kažukauskas
parent
0b13ee3e49
commit
ad19506836
@@ -37,6 +37,13 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
foreach (var tile in multiBrush.PossibleTiles())
|
foreach (var tile in multiBrush.PossibleTiles())
|
||||||
if (!templatedTerrainInfo.TryGetTerrainInfo(tile, out var _))
|
if (!templatedTerrainInfo.TryGetTerrainInfo(tile, out var _))
|
||||||
emitError($"Tileset {terrainInfoName} has invalid MultiBrush collection `{collectionName}`: tileset does not have tile {tile.Type},{tile.Index}");
|
emitError($"Tileset {terrainInfoName} has invalid MultiBrush collection `{collectionName}`: tileset does not have tile {tile.Type},{tile.Index}");
|
||||||
|
|
||||||
|
if (multiBrush.Segment != null)
|
||||||
|
{
|
||||||
|
// Validate start and end have a direction.
|
||||||
|
_ = multiBrush.Segment.StartDirection;
|
||||||
|
_ = multiBrush.Segment.EndDirection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) when (e is ArgumentException || e is InvalidOperationException)
|
catch (Exception e) when (e is ArgumentException || e is InvalidOperationException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -222,6 +222,18 @@ namespace OpenRA.Mods.Common.MapGenerator
|
|||||||
: (MatchesType(Start, matcher) || MatchesType(End, matcher));
|
: (MatchesType(Start, matcher) || MatchesType(End, matcher));
|
||||||
public bool HasEndType(string matcher)
|
public bool HasEndType(string matcher)
|
||||||
=> MatchesType(End, matcher);
|
=> MatchesType(End, matcher);
|
||||||
|
|
||||||
|
public static Direction TypeDirection(string type)
|
||||||
|
{
|
||||||
|
if (!Enum.TryParse(type.Split('.')[^1], out Direction direction))
|
||||||
|
throw new InvalidOperationException("MultiBrushSegment has invalid direction");
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction StartDirection
|
||||||
|
=> TypeDirection(Start);
|
||||||
|
public Direction EndDirection
|
||||||
|
=> TypeDirection(End);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A super template that can be used to paint both tiles and actors.</summary>
|
/// <summary>A super template that can be used to paint both tiles and actors.</summary>
|
||||||
|
|||||||
@@ -249,9 +249,7 @@ namespace OpenRA.Mods.Common.MapGenerator
|
|||||||
public readonly CVec Offset;
|
public readonly CVec Offset;
|
||||||
public readonly CVec Moves;
|
public readonly CVec Moves;
|
||||||
public readonly CVec[] RelativePoints;
|
public readonly CVec[] RelativePoints;
|
||||||
public readonly Direction[] Directions;
|
public readonly Direction EndDirection;
|
||||||
public readonly DirectionMask[] DirectionMasks;
|
|
||||||
public readonly DirectionMask[] ReverseDirectionMasks;
|
|
||||||
|
|
||||||
public TilingSegment(MultiBrush multiBrush, int startId, int endId)
|
public TilingSegment(MultiBrush multiBrush, int startId, int endId)
|
||||||
{
|
{
|
||||||
@@ -263,23 +261,13 @@ namespace OpenRA.Mods.Common.MapGenerator
|
|||||||
RelativePoints = multiBrush.Segment.Points
|
RelativePoints = multiBrush.Segment.Points
|
||||||
.Select(p => p - multiBrush.Segment.Points[0])
|
.Select(p => p - multiBrush.Segment.Points[0])
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
EndDirection = multiBrush.Segment.EndDirection;
|
||||||
|
|
||||||
Directions = new Direction[RelativePoints.Length];
|
|
||||||
DirectionMasks = new DirectionMask[RelativePoints.Length];
|
|
||||||
ReverseDirectionMasks = new DirectionMask[RelativePoints.Length];
|
|
||||||
|
|
||||||
// Last point has no direction.
|
|
||||||
Directions[^1] = Direction.None;
|
|
||||||
DirectionMasks[^1] = 0;
|
|
||||||
ReverseDirectionMasks[^1] = 0;
|
|
||||||
for (var i = 0; i < RelativePoints.Length - 1; i++)
|
for (var i = 0; i < RelativePoints.Length - 1; i++)
|
||||||
{
|
{
|
||||||
var direction = DirectionExts.FromCVec(RelativePoints[i + 1] - RelativePoints[i]);
|
var direction = DirectionExts.FromCVec(RelativePoints[i + 1] - RelativePoints[i]);
|
||||||
if (direction == Direction.None)
|
if (direction == Direction.None)
|
||||||
throw new ArgumentException("MultiBrushSegment has duplicate points in sequence");
|
throw new ArgumentException("MultiBrushSegment has duplicate points in sequence");
|
||||||
Directions[i] = direction;
|
|
||||||
DirectionMasks[i] = direction.ToMask();
|
|
||||||
ReverseDirectionMasks[i] = direction.Reverse().ToMask();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,6 +311,7 @@ namespace OpenRA.Mods.Common.MapGenerator
|
|||||||
// order for a transition to be allowed at all, it must satisfy some constraints:
|
// order for a transition to be allowed at all, it must satisfy some constraints:
|
||||||
//
|
//
|
||||||
// - It must not regress backward along the path (but no immediate progress is OK).
|
// - It must not regress backward along the path (but no immediate progress is OK).
|
||||||
|
// - If it makes exactly zero progress, it must end facing towards increasing progress.
|
||||||
// - It must not deviate at any point in the segment beyond MaxDeviation from the path.
|
// - It must not deviate at any point in the segment beyond MaxDeviation from the path.
|
||||||
// - It must not skip to much later path points (which may be within MaxDeviation).
|
// - It must not skip to much later path points (which may be within MaxDeviation).
|
||||||
//
|
//
|
||||||
@@ -688,6 +677,24 @@ namespace OpenRA.Mods.Common.MapGenerator
|
|||||||
return MaxCost;
|
return MaxCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's a zero-progress segment without deviation, only allow it if it directs
|
||||||
|
// towards a positive progression.
|
||||||
|
if (lowProgressionAcc == 0 && highProgressionAcc == 0)
|
||||||
|
{
|
||||||
|
var point = to;
|
||||||
|
var pointNext = to + segment.EndDirection.ToCVec();
|
||||||
|
if (!deviations.ContainsXY(pointNext.X, pointNext.Y) || deviations[pointNext.X, pointNext.Y] == OverDeviation)
|
||||||
|
{
|
||||||
|
// Projected point escapes bounds or is in an excluded position.
|
||||||
|
return MaxCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
lowProgressionAcc = Progress(lowProgress[point.X, point.Y], lowProgress[pointNext.X, pointNext.Y]);
|
||||||
|
highProgressionAcc = Progress(highProgress[point.X, point.Y], highProgress[pointNext.X, pointNext.Y]);
|
||||||
|
if (lowProgressionAcc < 0 || highProgressionAcc < 0 || (lowProgressionAcc == 0 && highProgressionAcc == 0))
|
||||||
|
return MaxCost;
|
||||||
|
}
|
||||||
|
|
||||||
// Satisfies all requirements.
|
// Satisfies all requirements.
|
||||||
return deviationAcc;
|
return deviationAcc;
|
||||||
}
|
}
|
||||||
@@ -896,7 +903,7 @@ namespace OpenRA.Mods.Common.MapGenerator
|
|||||||
(to, toTypeId) = TraceBackStep(to, toTypeId, true);
|
(to, toTypeId) = TraceBackStep(to, toTypeId, true);
|
||||||
|
|
||||||
// No need to check direction. If that is an issue, I have bigger problems to worry about.
|
// No need to check direction. If that is an issue, I have bigger problems to worry about.
|
||||||
while (to != pathStart)
|
while (to != pathStart || toTypeId != pathStartTypeId)
|
||||||
(to, toTypeId) = TraceBackStep(to, toTypeId, false);
|
(to, toTypeId) = TraceBackStep(to, toTypeId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user