Place map generator spawns more intuitively
The map generators previously placed spawns in an order that was somewhat arbitrary and unintuitive to users. This change provides and makes use of a Terraformer utility to reorder spawns, based on their location, so that they typically make more sense to users selecting spawns from a lobby. Added to ExperimentalMapGenerator (RA, CnC), and D2kMapGenerator. Note that as this changes the output of the map generator for a given selection of settings. (Replays using generated maps from older versions will lose compatibility and no longer load.)
This commit is contained in:
committed by
Gustas Kažukauskas
parent
1300ecc18f
commit
2cf2d7eed7
@@ -2243,5 +2243,63 @@ namespace OpenRA.Mods.Common.MapGenerator
|
||||
|
||||
return decorable;
|
||||
}
|
||||
|
||||
/// <summary>Reorder mpspawn positions to make them more intuitive within a lobby.</summary>
|
||||
public void ReorderPlayerSpawns()
|
||||
{
|
||||
// Find and take the actors out of the ActorPlans list.
|
||||
var mpspawns = ActorPlans.Where(a => a.Reference.Type == "mpspawn").ToList();
|
||||
if (mpspawns.Count <= 1)
|
||||
return;
|
||||
|
||||
ActorPlans.RemoveAll(a => a.Reference.Type == "mpspawn");
|
||||
|
||||
// Sort the spawns clockwise around the center
|
||||
var wCenter = CellLayerUtils.Center(Map);
|
||||
(int Angle, long RadiusSq) PolarPosition(ActorPlan plan)
|
||||
{
|
||||
var locationFromCenter = plan.WPosCenterLocation - wCenter;
|
||||
var wangle = WAngle.ArcTan(locationFromCenter.Y, locationFromCenter.X);
|
||||
var radiusSq = locationFromCenter.LengthSquared;
|
||||
return (wangle.Angle, radiusSq);
|
||||
}
|
||||
|
||||
mpspawns = mpspawns.OrderBy(PolarPosition).ToList();
|
||||
|
||||
// Find a reasonable ("A") spawn. It should:
|
||||
// - Have the largest possible preceeding angular gap from the preceeding spawn.
|
||||
// - (Tie breaker) should be close to the left.
|
||||
// - (Tie breaker 2) should be close to the top.
|
||||
//
|
||||
// Note that this offers a strong suggestion of spawn groupings for teams. This works
|
||||
// well for the vast majority of cases, but is ultimately subjective. In some rare
|
||||
// cases, it may suggest teams that seem unusual compared to alternatives, for example:
|
||||
// - long but orderly straight lines of spawns (rather than tight clusters);
|
||||
// - clustering by euclidean distance rather than polar angle;
|
||||
// - division according to land masses or terrain barriers.
|
||||
var previousPolar = PolarPosition(mpspawns[^1]);
|
||||
var choices = new List<(int Gap, int X, int Y, int Index)>();
|
||||
for (var i = 0; i < mpspawns.Count; i++)
|
||||
{
|
||||
var thisPolar = PolarPosition(mpspawns[i]);
|
||||
var gap = (thisPolar.Angle - previousPolar.Angle + 1024) % 1024;
|
||||
var location = mpspawns[i].WPosLocation;
|
||||
choices.Add((gap, location.X, location.Y, i));
|
||||
previousPolar = thisPolar;
|
||||
}
|
||||
|
||||
const int ComparisonThreshold = 2;
|
||||
var bestGap = choices.Max(c => c.Gap);
|
||||
var bestIndex = choices
|
||||
.Where(c => c.Gap >= bestGap - ComparisonThreshold)
|
||||
.Min(c => (c.X, c.Y, c.Index))
|
||||
.Index;
|
||||
|
||||
// Rotate the spawns so that our "A" spawn is first.
|
||||
mpspawns = [.. mpspawns[bestIndex..], .. mpspawns[..bestIndex]];
|
||||
|
||||
// Put them back into the ActorPlans list.
|
||||
ActorPlans.AddRange(mpspawns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,6 +947,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
// Cosmetically repaint tiles
|
||||
terraformer.RepaintTiles(repaintRandom, param.RepaintTiles);
|
||||
|
||||
terraformer.ReorderPlayerSpawns();
|
||||
terraformer.BakeMap();
|
||||
|
||||
return map;
|
||||
|
||||
@@ -610,6 +610,7 @@ namespace OpenRA.Mods.D2k.Traits
|
||||
}
|
||||
}
|
||||
|
||||
terraformer.ReorderPlayerSpawns();
|
||||
terraformer.BakeMap();
|
||||
|
||||
return map;
|
||||
|
||||
Reference in New Issue
Block a user