Add experimental RA procedural map generator
Add an experimental procedural map generator for the Red Alert mod, along with supporting code that may assist in the development of map generators for other mods. Map generation may be accessed as a tool in the Map Editor. This change does not presently introduce direct lobby options for generated maps. Features: - Terrain with land, water, beaches, cliffs, roads, debris, and trees. - Placement of mpspawns, neutral buildings, and resources. - Rotational and mirror symmetry options. - Various configurable parameters with presets. - Deterministic with configurable seed. - Performant.
This commit is contained in:
@@ -10,9 +10,11 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Mods.Common.MapGenerator;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
@@ -79,6 +81,10 @@ namespace OpenRA.Mods.Common.Terrain
|
||||
|
||||
[FieldLoader.Ignore]
|
||||
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
|
||||
[FieldLoader.Ignore]
|
||||
public readonly IReadOnlyDictionary<TemplateSegment, TerrainTemplateInfo> SegmentsToTemplates;
|
||||
[FieldLoader.Ignore]
|
||||
public readonly IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> MultiBrushCollections;
|
||||
|
||||
[FieldLoader.Ignore]
|
||||
public readonly TerrainTypeInfo[] TerrainInfo;
|
||||
@@ -117,6 +123,20 @@ namespace OpenRA.Mods.Common.Terrain
|
||||
// Templates
|
||||
Templates = yaml["Templates"].ToDictionary().Values
|
||||
.Select(y => (TerrainTemplateInfo)new DefaultTerrainTemplateInfo(this, y)).ToDictionary(t => t.Id);
|
||||
|
||||
SegmentsToTemplates = ImmutableDictionary.CreateRange(
|
||||
Templates.Values.SelectMany(
|
||||
template => template.Segments.Select(
|
||||
segment => new KeyValuePair<TemplateSegment, TerrainTemplateInfo>(segment, template))));
|
||||
|
||||
MultiBrushCollections =
|
||||
yaml.TryGetValue("MultiBrushCollections", out var collectionDefinitions)
|
||||
? collectionDefinitions.ToDictionary()
|
||||
.Select(kv => new KeyValuePair<string, IEnumerable<MultiBrushInfo>>(
|
||||
kv.Key,
|
||||
MultiBrushInfo.ParseCollection(kv.Value)))
|
||||
.ToImmutableDictionary()
|
||||
: ImmutableDictionary<string, IEnumerable<MultiBrushInfo>>.Empty;
|
||||
}
|
||||
|
||||
public TerrainTypeInfo this[byte index] => TerrainInfo[index];
|
||||
@@ -167,6 +187,8 @@ namespace OpenRA.Mods.Common.Terrain
|
||||
|
||||
string[] ITemplatedTerrainInfo.EditorTemplateOrder => EditorTemplateOrder;
|
||||
IReadOnlyDictionary<ushort, TerrainTemplateInfo> ITemplatedTerrainInfo.Templates => Templates;
|
||||
IReadOnlyDictionary<TemplateSegment, TerrainTemplateInfo> ITemplatedTerrainInfo.SegmentsToTemplates => SegmentsToTemplates;
|
||||
IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> ITemplatedTerrainInfo.MultiBrushCollections => MultiBrushCollections;
|
||||
|
||||
void ITerrainInfoNotifyMapCreated.MapCreated(Map map)
|
||||
{
|
||||
|
||||
63
OpenRA.Mods.Common/Terrain/TemplateSegment.cs
Normal file
63
OpenRA.Mods.Common/Terrain/TemplateSegment.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace OpenRA.Mods.Common.Terrain
|
||||
{
|
||||
/// <summary>
|
||||
/// Information about how certain templates (like cliffs, beaches, roads) link together.
|
||||
/// </summary>
|
||||
public class TemplateSegment
|
||||
{
|
||||
public readonly string Start;
|
||||
public readonly string Inner;
|
||||
public readonly string End;
|
||||
|
||||
/// <summary>
|
||||
/// Point sequence, where points are -X-Y corners of template tiles.
|
||||
/// </summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly CVec[] Points;
|
||||
|
||||
public TemplateSegment(MiniYaml my)
|
||||
{
|
||||
FieldLoader.Load(this, my);
|
||||
{
|
||||
// Unlike FieldLoader.ParseInt2Array, whitespace is ignored.
|
||||
var value = my.NodeWithKey("Points").Value.Value;
|
||||
var parts = Regex.Replace(value, @"\s+", string.Empty)
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length % 2 != 0)
|
||||
FieldLoader.InvalidValueAction(value, typeof(int2[]), "Points");
|
||||
Points = new CVec[parts.Length / 2];
|
||||
for (var i = 0; i < Points.Length; i++)
|
||||
Points[i] = new CVec(Exts.ParseInt32Invariant(parts[2 * i]), Exts.ParseInt32Invariant(parts[2 * i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool MatchesType(string type, string matcher)
|
||||
{
|
||||
if (type == matcher)
|
||||
return true;
|
||||
|
||||
return type.StartsWith($"{matcher}.", StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
public bool HasStartType(string matcher)
|
||||
=> MatchesType(Start, matcher);
|
||||
public bool HasInnerType(string matcher)
|
||||
=> MatchesType(Inner, matcher);
|
||||
public bool HasEndType(string matcher)
|
||||
=> MatchesType(End, matcher);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Mods.Common.MapGenerator;
|
||||
|
||||
namespace OpenRA.Mods.Common.Terrain
|
||||
{
|
||||
@@ -17,6 +19,8 @@ namespace OpenRA.Mods.Common.Terrain
|
||||
{
|
||||
string[] EditorTemplateOrder { get; }
|
||||
IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates { get; }
|
||||
IReadOnlyDictionary<TemplateSegment, TerrainTemplateInfo> SegmentsToTemplates { get; }
|
||||
IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> MultiBrushCollections { get; }
|
||||
}
|
||||
|
||||
public interface ITerrainInfoNotifyMapCreated : ITerrainInfo
|
||||
@@ -33,6 +37,8 @@ namespace OpenRA.Mods.Common.Terrain
|
||||
|
||||
readonly TerrainTileInfo[] tileInfo;
|
||||
|
||||
public readonly TemplateSegment[] Segments;
|
||||
|
||||
public TerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my)
|
||||
{
|
||||
FieldLoader.Load(this, my);
|
||||
@@ -72,6 +78,19 @@ namespace OpenRA.Mods.Common.Terrain
|
||||
tileInfo[key] = LoadTileInfo(terrainInfo, node.Value);
|
||||
}
|
||||
}
|
||||
|
||||
var segmentsNode = my.NodeWithKeyOrDefault("Segments");
|
||||
if (segmentsNode != null)
|
||||
{
|
||||
Segments = new TemplateSegment[segmentsNode.Value.Nodes.Length];
|
||||
var i = 0;
|
||||
foreach (var segmentNode in segmentsNode.Value.Nodes)
|
||||
Segments[i++] = new TemplateSegment(segmentNode.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Segments = Array.Empty<TemplateSegment>();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual TerrainTileInfo LoadTileInfo(ITerrainInfo terrainInfo, MiniYaml my)
|
||||
|
||||
Reference in New Issue
Block a user