Files
OpenRA/OpenRA.Mods.Common/Terrain/TerrainInfo.cs
Ashley Newson a2096b6768 Use MultiBrush as the input and output of TilingPath
This generalizes TilingPath to accept MultiBrushes instead of templates
and makes the output also a MultiBrush. As a result:

- Composite or faked templates can be used in tiling. This will (in a
  later change) be used to support CnC beaches outside of the desert
  tileset.
- The tiling result can be previewed without committing it onto the map,
  making it more viable for adoption as an editor tool.
- The full shape of the tiling result can be inspected without messily
  reading it back off of the map, simplifying some map generator logic.
- Separates map generation-specific segment definitions from the core
  template definitions.
- As MultiBrushes can be many-to-one with templates, there is no need to
  have a one-to-many template-segment relationship. Multi-segment
  templates, such as roads, now use multiple brushes instead. This
  simplifies the logic of segment consumers.

Previously, MultiBrushes which contained no offset information in their
definitions were automatically aligned such that the first tile was
under 0,0. This is no longer the case. This was previously done for use
in MultiBrush.PaintArea to improve tile packing success, but the
alignment is now handled automatically by PaintArea instead.

Due to some impure refactoring which changes the randomization of
tiling choices, map generation results may change.
2025-04-18 18:19:11 +03:00

108 lines
3.2 KiB
C#

#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.Collections.Generic;
using OpenRA.Mods.Common.MapGenerator;
namespace OpenRA.Mods.Common.Terrain
{
public interface ITemplatedTerrainInfo : ITerrainInfo
{
string[] EditorTemplateOrder { get; }
IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates { get; }
IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> MultiBrushCollections { get; }
}
public interface ITerrainInfoNotifyMapCreated : ITerrainInfo
{
void MapCreated(Map map);
}
public class TerrainTemplateInfo
{
public readonly ushort Id;
public readonly int2 Size;
public readonly bool PickAny;
public readonly string[] Categories;
readonly TerrainTileInfo[] tileInfo;
public TerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my)
{
FieldLoader.Load(this, my);
var nodes = my.NodeWithKey("Tiles").Value.Nodes;
if (!PickAny)
{
tileInfo = new TerrainTileInfo[Size.X * Size.Y];
foreach (var node in nodes)
{
if (!int.TryParse(node.Key, out var key))
throw new YamlException(
$"Tileset `{terrainInfo.Id}` template `{Id}` defines a frame `{node.Key}` that is not a valid integer.");
if (key < 0 || key >= tileInfo.Length)
throw new YamlException(
$"Tileset `{terrainInfo.Id}` template `{Id}` references frame {key}, " +
$"but only [0..{tileInfo.Length - 1}] are valid for a {Size.X}x{Size.Y} Size template.");
tileInfo[key] = LoadTileInfo(terrainInfo, node.Value);
}
}
else
{
tileInfo = new TerrainTileInfo[nodes.Length];
var i = 0;
foreach (var node in nodes)
{
if (!int.TryParse(node.Key, out var key))
throw new YamlException($"Tileset `{terrainInfo.Id}` template `{Id}` defines a frame `{node.Key}` that is not a valid integer.");
if (key != i++)
throw new YamlException($"Tileset `{terrainInfo.Id}` template `{Id}` is missing a definition for frame {i - 1}.");
tileInfo[key] = LoadTileInfo(terrainInfo, node.Value);
}
}
}
protected virtual TerrainTileInfo LoadTileInfo(ITerrainInfo terrainInfo, MiniYaml my)
{
var tile = new TerrainTileInfo();
FieldLoader.Load(tile, my);
// Terrain type must be converted from a string to an index
tile.GetType().GetField(nameof(tile.TerrainType)).SetValue(tile, terrainInfo.GetTerrainIndex(my.Value));
// Fall back to the terrain-type color if necessary
var overrideColor = terrainInfo.TerrainTypes[tile.TerrainType].Color;
if (tile.MinColor == default)
tile.GetType().GetField(nameof(tile.MinColor)).SetValue(tile, overrideColor);
if (tile.MaxColor == default)
tile.GetType().GetField(nameof(tile.MaxColor)).SetValue(tile, overrideColor);
return tile;
}
public TerrainTileInfo this[int index] => tileInfo[index];
public bool Contains(int index)
{
return index >= 0 && index < tileInfo.Length;
}
public int TilesCount => tileInfo.Length;
}
}