Support relaxed end points in TilingPath

Whilst TilingPath has always allowed deviating from the path points
between the start and end point, it didn't allow deviation from the
start or end points themselves. This change allows the end point to
deviate if the tiling would otherwise fail. The start point remains
strictly positioned, as before. Loop end points (which must necessarily
match the start) also remain strict.

The ExperimentalMapGenerator is modified to benefit from the relaxed
tiling constraints. As a result, failed map generation due to tiling
failures is much rarer, and will make some otherwise very inflexible
template categories viable for tiling.

Includes some impure refactoring around the treatment of start,
intermediate, and end segments. As such, this changes map generation
output, even for maps which already tiled perfectly.

A previous workaround used for CnC road templates, whereby additional
segments were defined for road ending templates, is now redundant and
cleaned up.
This commit is contained in:
Ashley Newson
2025-04-06 23:24:42 +01:00
committed by Gustas Kažukauskas
parent b2acc653a0
commit f09959ecba
7 changed files with 204 additions and 226 deletions

View File

@@ -15,6 +15,7 @@ using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA.Mods.Common.MapGenerator
{
@@ -1502,5 +1503,34 @@ namespace OpenRA.Mods.Common.MapGenerator
return matrix;
}
/// <summary>
/// Rank all cell values and select the best (greatest compared) value.
/// If there are equally good best candidates, choose one at random.
/// </summary>
public static (int2 MPos, T Value) FindRandomBest<T>(
Matrix<T> matrix,
MersenneTwister random,
Comparison<T> comparison)
{
var candidates = new List<int2>();
var best = matrix[new int2(0, 0)];
for (var y = 0; y < matrix.Size.Y; y++)
for (var x = 0; x < matrix.Size.X; x++)
{
var rank = comparison(matrix[x, y], best);
if (rank > 0)
{
best = matrix[x, y];
candidates.Clear();
}
if (rank >= 0)
candidates.Add(new int2(x, y));
}
var choice = candidates[random.Next(candidates.Count)];
return (choice, best);
}
}
}