Move default tileset parsing to Mods.Common.
This commit is contained in:
68
OpenRA.Game/Map/TerrainInfo.cs
Normal file
68
OpenRA.Game/Map/TerrainInfo.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||||
|
* 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.FileSystem;
|
||||||
|
using OpenRA.Primitives;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA
|
||||||
|
{
|
||||||
|
public interface ITerrainLoader
|
||||||
|
{
|
||||||
|
ITerrainInfo ParseTerrain(IReadOnlyFileSystem fileSystem, string path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITerrainInfo
|
||||||
|
{
|
||||||
|
string Id { get; }
|
||||||
|
TerrainTypeInfo[] TerrainTypes { get; }
|
||||||
|
TerrainTileInfo GetTerrainInfo(TerrainTile r);
|
||||||
|
bool TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info);
|
||||||
|
byte GetTerrainIndex(string type);
|
||||||
|
byte GetTerrainIndex(TerrainTile r);
|
||||||
|
TerrainTile DefaultTerrainTile { get; }
|
||||||
|
|
||||||
|
Color[] HeightDebugColors { get; }
|
||||||
|
IEnumerable<Color> RestrictedPlayerColors { get; }
|
||||||
|
float MinHeightColorBrightness { get; }
|
||||||
|
float MaxHeightColorBrightness { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TerrainTileInfo
|
||||||
|
{
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
public readonly byte TerrainType = byte.MaxValue;
|
||||||
|
public readonly byte Height;
|
||||||
|
public readonly byte RampType;
|
||||||
|
public readonly Color MinColor;
|
||||||
|
public readonly Color MaxColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TerrainTypeInfo
|
||||||
|
{
|
||||||
|
public readonly string Type;
|
||||||
|
public readonly BitSet<TargetableType> TargetTypes;
|
||||||
|
public readonly HashSet<string> AcceptsSmudgeType = new HashSet<string>();
|
||||||
|
public readonly Color Color;
|
||||||
|
public readonly bool RestrictPlayerColor = false;
|
||||||
|
public readonly string CustomCursor;
|
||||||
|
|
||||||
|
public TerrainTypeInfo(MiniYaml my) { FieldLoader.Load(this, my); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: Temporary placeholder to avoid having to change all the traits that reference this constant.
|
||||||
|
// This can be removed after the palette references have been moved from traits to sequences.
|
||||||
|
public class TileSet
|
||||||
|
{
|
||||||
|
public const string TerrainPaletteInternalName = "terrain";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,311 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
|
||||||
* 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 System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using OpenRA.FileSystem;
|
|
||||||
using OpenRA.Primitives;
|
|
||||||
using OpenRA.Support;
|
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA
|
|
||||||
{
|
|
||||||
public interface ITerrainLoader
|
|
||||||
{
|
|
||||||
ITerrainInfo ParseTerrain(IReadOnlyFileSystem fileSystem, string path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITerrainInfo
|
|
||||||
{
|
|
||||||
string Id { get; }
|
|
||||||
TerrainTypeInfo[] TerrainTypes { get; }
|
|
||||||
TerrainTileInfo GetTerrainInfo(TerrainTile r);
|
|
||||||
bool TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info);
|
|
||||||
byte GetTerrainIndex(string type);
|
|
||||||
byte GetTerrainIndex(TerrainTile r);
|
|
||||||
TerrainTile DefaultTerrainTile { get; }
|
|
||||||
|
|
||||||
Color[] HeightDebugColors { get; }
|
|
||||||
IEnumerable<Color> RestrictedPlayerColors { get; }
|
|
||||||
float MinHeightColorBrightness { get; }
|
|
||||||
float MaxHeightColorBrightness { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITemplatedTerrainInfo : ITerrainInfo
|
|
||||||
{
|
|
||||||
string[] EditorTemplateOrder { get; }
|
|
||||||
IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITerrainInfoNotifyMapCreated : ITerrainInfo
|
|
||||||
{
|
|
||||||
void MapCreated(Map map);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TerrainTileInfo
|
|
||||||
{
|
|
||||||
[FieldLoader.Ignore]
|
|
||||||
public readonly byte TerrainType = byte.MaxValue;
|
|
||||||
public readonly byte Height;
|
|
||||||
public readonly byte RampType;
|
|
||||||
public readonly Color MinColor;
|
|
||||||
public readonly Color MaxColor;
|
|
||||||
public readonly float ZOffset = 0.0f;
|
|
||||||
public readonly float ZRamp = 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TerrainTypeInfo
|
|
||||||
{
|
|
||||||
public readonly string Type;
|
|
||||||
public readonly BitSet<TargetableType> TargetTypes;
|
|
||||||
public readonly HashSet<string> AcceptsSmudgeType = new HashSet<string>();
|
|
||||||
public readonly Color Color;
|
|
||||||
public readonly bool RestrictPlayerColor = false;
|
|
||||||
public readonly string CustomCursor;
|
|
||||||
|
|
||||||
public TerrainTypeInfo(MiniYaml my) { FieldLoader.Load(this, my); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TerrainTemplateInfo
|
|
||||||
{
|
|
||||||
public readonly ushort Id;
|
|
||||||
public readonly string[] Images;
|
|
||||||
public readonly int[] Frames;
|
|
||||||
public readonly int2 Size;
|
|
||||||
public readonly bool PickAny;
|
|
||||||
public readonly string[] Categories;
|
|
||||||
public readonly string Palette;
|
|
||||||
|
|
||||||
readonly TerrainTileInfo[] tileInfo;
|
|
||||||
|
|
||||||
public TerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my)
|
|
||||||
{
|
|
||||||
FieldLoader.Load(this, my);
|
|
||||||
|
|
||||||
var nodes = my.ToDictionary()["Tiles"].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 `{0}` template `{1}` defines a frame `{2}` that is not a valid integer.".F(terrainInfo.Id, Id, node.Key));
|
|
||||||
|
|
||||||
if (key < 0 || key >= tileInfo.Length)
|
|
||||||
throw new YamlException("Tileset `{0}` template `{1}` references frame {2}, but only [0..{3}] are valid for a {4}x{5} Size template.".F(terrainInfo.Id, Id, key, tileInfo.Length - 1, Size.X, Size.Y));
|
|
||||||
|
|
||||||
tileInfo[key] = LoadTileInfo(terrainInfo, node.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tileInfo = new TerrainTileInfo[nodes.Count];
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
if (!int.TryParse(node.Key, out var key))
|
|
||||||
throw new YamlException("Tileset `{0}` template `{1}` defines a frame `{2}` that is not a valid integer.".F(terrainInfo.Id, Id, node.Key));
|
|
||||||
|
|
||||||
if (key != i++)
|
|
||||||
throw new YamlException("Tileset `{0}` template `{1}` is missing a definition for frame {2}.".F(terrainInfo.Id, Id, i - 1));
|
|
||||||
|
|
||||||
tileInfo[key] = LoadTileInfo(terrainInfo, node.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static 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("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("MinColor").SetValue(tile, overrideColor);
|
|
||||||
|
|
||||||
if (tile.MaxColor == default)
|
|
||||||
tile.GetType().GetField("MaxColor").SetValue(tile, overrideColor);
|
|
||||||
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerrainTileInfo this[int index] { get { return tileInfo[index]; } }
|
|
||||||
|
|
||||||
public bool Contains(int index)
|
|
||||||
{
|
|
||||||
return index >= 0 && index < tileInfo.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int TilesCount
|
|
||||||
{
|
|
||||||
get { return tileInfo.Length; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TileSet : ITemplatedTerrainInfo, ITerrainInfoNotifyMapCreated
|
|
||||||
{
|
|
||||||
public const string TerrainPaletteInternalName = "terrain";
|
|
||||||
|
|
||||||
public readonly string Name;
|
|
||||||
public readonly string Id;
|
|
||||||
public readonly int SheetSize = 512;
|
|
||||||
public readonly Color[] HeightDebugColors = new[] { Color.Red };
|
|
||||||
public readonly string[] EditorTemplateOrder;
|
|
||||||
public readonly bool IgnoreTileSpriteOffsets;
|
|
||||||
public readonly bool EnableDepth = false;
|
|
||||||
public readonly float MinHeightColorBrightness = 1.0f;
|
|
||||||
public readonly float MaxHeightColorBrightness = 1.0f;
|
|
||||||
|
|
||||||
[FieldLoader.Ignore]
|
|
||||||
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
|
|
||||||
|
|
||||||
[FieldLoader.Ignore]
|
|
||||||
public readonly TerrainTypeInfo[] TerrainInfo;
|
|
||||||
readonly Dictionary<string, byte> terrainIndexByType = new Dictionary<string, byte>();
|
|
||||||
readonly byte defaultWalkableTerrainIndex;
|
|
||||||
|
|
||||||
public TileSet(IReadOnlyFileSystem fileSystem, string filepath)
|
|
||||||
{
|
|
||||||
var yaml = MiniYaml.FromStream(fileSystem.Open(filepath), filepath)
|
|
||||||
.ToDictionary(x => x.Key, x => x.Value);
|
|
||||||
|
|
||||||
// General info
|
|
||||||
FieldLoader.Load(this, yaml["General"]);
|
|
||||||
|
|
||||||
// TerrainTypes
|
|
||||||
TerrainInfo = yaml["Terrain"].ToDictionary().Values
|
|
||||||
.Select(y => new TerrainTypeInfo(y))
|
|
||||||
.OrderBy(tt => tt.Type)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
if (TerrainInfo.Length >= byte.MaxValue)
|
|
||||||
throw new YamlException("Too many terrain types.");
|
|
||||||
|
|
||||||
for (byte i = 0; i < TerrainInfo.Length; i++)
|
|
||||||
{
|
|
||||||
var tt = TerrainInfo[i].Type;
|
|
||||||
|
|
||||||
if (terrainIndexByType.ContainsKey(tt))
|
|
||||||
throw new YamlException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
|
|
||||||
|
|
||||||
terrainIndexByType.Add(tt, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultWalkableTerrainIndex = GetTerrainIndex("Clear");
|
|
||||||
|
|
||||||
// Templates
|
|
||||||
Templates = yaml["Templates"].ToDictionary().Values
|
|
||||||
.Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id).AsReadOnly();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TileSet(string name, string id, TerrainTypeInfo[] terrainInfo)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Id = id;
|
|
||||||
TerrainInfo = terrainInfo;
|
|
||||||
|
|
||||||
if (TerrainInfo.Length >= byte.MaxValue)
|
|
||||||
throw new InvalidDataException("Too many terrain types.");
|
|
||||||
|
|
||||||
for (byte i = 0; i < terrainInfo.Length; i++)
|
|
||||||
{
|
|
||||||
var tt = terrainInfo[i].Type;
|
|
||||||
if (terrainIndexByType.ContainsKey(tt))
|
|
||||||
throw new InvalidDataException("Duplicate terrain type '{0}'.".F(tt));
|
|
||||||
|
|
||||||
terrainIndexByType.Add(tt, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultWalkableTerrainIndex = GetTerrainIndex("Clear");
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerrainTypeInfo this[byte index]
|
|
||||||
{
|
|
||||||
get { return TerrainInfo[index]; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetTerrainIndex(string type, out byte index)
|
|
||||||
{
|
|
||||||
return terrainIndexByType.TryGetValue(type, out index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte GetTerrainIndex(string type)
|
|
||||||
{
|
|
||||||
if (terrainIndexByType.TryGetValue(type, out var index))
|
|
||||||
return index;
|
|
||||||
|
|
||||||
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte GetTerrainIndex(TerrainTile r)
|
|
||||||
{
|
|
||||||
var tile = Templates[r.Type][r.Index];
|
|
||||||
if (tile.TerrainType != byte.MaxValue)
|
|
||||||
return tile.TerrainType;
|
|
||||||
|
|
||||||
return defaultWalkableTerrainIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerrainTileInfo GetTileInfo(TerrainTile r)
|
|
||||||
{
|
|
||||||
return Templates[r.Type][r.Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetTileInfo(TerrainTile r, out TerrainTileInfo info)
|
|
||||||
{
|
|
||||||
if (!Templates.TryGetValue(r.Type, out var tpl) || !tpl.Contains(r.Index))
|
|
||||||
{
|
|
||||||
info = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = tpl[r.Index];
|
|
||||||
return info != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
string ITerrainInfo.Id { get { return Id; } }
|
|
||||||
TerrainTypeInfo[] ITerrainInfo.TerrainTypes { get { return TerrainInfo; } }
|
|
||||||
TerrainTileInfo ITerrainInfo.GetTerrainInfo(TerrainTile r) { return GetTileInfo(r); }
|
|
||||||
bool ITerrainInfo.TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info) { return TryGetTileInfo(r, out info); }
|
|
||||||
Color[] ITerrainInfo.HeightDebugColors { get { return HeightDebugColors; } }
|
|
||||||
IEnumerable<Color> ITerrainInfo.RestrictedPlayerColors { get { return TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); } }
|
|
||||||
float ITerrainInfo.MinHeightColorBrightness { get { return MinHeightColorBrightness; } }
|
|
||||||
float ITerrainInfo.MaxHeightColorBrightness { get { return MaxHeightColorBrightness; } }
|
|
||||||
TerrainTile ITerrainInfo.DefaultTerrainTile { get { return new TerrainTile(Templates.First().Key, 0); } }
|
|
||||||
|
|
||||||
string[] ITemplatedTerrainInfo.EditorTemplateOrder { get { return EditorTemplateOrder; } }
|
|
||||||
IReadOnlyDictionary<ushort, TerrainTemplateInfo> ITemplatedTerrainInfo.Templates { get { return Templates; } }
|
|
||||||
|
|
||||||
void ITerrainInfoNotifyMapCreated.MapCreated(Map map)
|
|
||||||
{
|
|
||||||
// Randomize PickAny tile variants
|
|
||||||
var r = new MersenneTwister();
|
|
||||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
|
||||||
{
|
|
||||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
|
||||||
{
|
|
||||||
var type = map.Tiles[new MPos(i, j)].Type;
|
|
||||||
if (!Templates.TryGetValue(type, out var template) || !template.PickAny)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
map.Tiles[new MPos(i, j)] = new TerrainTile(type, (byte)r.Next(0, template.TilesCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@ using OpenRA.FileSystem;
|
|||||||
using OpenRA.Mods.Cnc.FileFormats;
|
using OpenRA.Mods.Cnc.FileFormats;
|
||||||
using OpenRA.Mods.Common;
|
using OpenRA.Mods.Common;
|
||||||
using OpenRA.Mods.Common.FileFormats;
|
using OpenRA.Mods.Common.FileFormats;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Widgets
|
namespace OpenRA.Mods.Common.Widgets
|
||||||
|
|||||||
@@ -9,8 +9,12 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.FileSystem;
|
using OpenRA.FileSystem;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Primitives;
|
||||||
|
using OpenRA.Support;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Terrain
|
namespace OpenRA.Mods.Common.Terrain
|
||||||
{
|
{
|
||||||
@@ -20,7 +24,166 @@ namespace OpenRA.Mods.Common.Terrain
|
|||||||
|
|
||||||
public ITerrainInfo ParseTerrain(IReadOnlyFileSystem fileSystem, string path)
|
public ITerrainInfo ParseTerrain(IReadOnlyFileSystem fileSystem, string path)
|
||||||
{
|
{
|
||||||
return new TileSet(fileSystem, path);
|
return new DefaultTerrain(fileSystem, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultTerrainTileInfo : TerrainTileInfo
|
||||||
|
{
|
||||||
|
public readonly float ZOffset = 0.0f;
|
||||||
|
public readonly float ZRamp = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultTerrainTemplateInfo : TerrainTemplateInfo
|
||||||
|
{
|
||||||
|
public readonly string[] Images;
|
||||||
|
public readonly int[] Frames;
|
||||||
|
public readonly string Palette;
|
||||||
|
|
||||||
|
public DefaultTerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my)
|
||||||
|
: base(terrainInfo, my) { }
|
||||||
|
|
||||||
|
protected override TerrainTileInfo LoadTileInfo(ITerrainInfo terrainInfo, MiniYaml my)
|
||||||
|
{
|
||||||
|
var tile = new DefaultTerrainTileInfo();
|
||||||
|
FieldLoader.Load(tile, my);
|
||||||
|
|
||||||
|
// Terrain type must be converted from a string to an index
|
||||||
|
tile.GetType().GetField("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("MinColor").SetValue(tile, overrideColor);
|
||||||
|
|
||||||
|
if (tile.MaxColor == default)
|
||||||
|
tile.GetType().GetField("MaxColor").SetValue(tile, overrideColor);
|
||||||
|
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultTerrain : ITemplatedTerrainInfo, ITerrainInfoNotifyMapCreated
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly string Id;
|
||||||
|
public readonly int SheetSize = 512;
|
||||||
|
public readonly Color[] HeightDebugColors = { Color.Red };
|
||||||
|
public readonly string[] EditorTemplateOrder;
|
||||||
|
public readonly bool IgnoreTileSpriteOffsets;
|
||||||
|
public readonly bool EnableDepth = false;
|
||||||
|
public readonly float MinHeightColorBrightness = 1.0f;
|
||||||
|
public readonly float MaxHeightColorBrightness = 1.0f;
|
||||||
|
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
|
||||||
|
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
public readonly TerrainTypeInfo[] TerrainInfo;
|
||||||
|
readonly Dictionary<string, byte> terrainIndexByType = new Dictionary<string, byte>();
|
||||||
|
readonly byte defaultWalkableTerrainIndex;
|
||||||
|
|
||||||
|
public DefaultTerrain(IReadOnlyFileSystem fileSystem, string filepath)
|
||||||
|
{
|
||||||
|
var yaml = MiniYaml.FromStream(fileSystem.Open(filepath), filepath)
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
|
||||||
|
// General info
|
||||||
|
FieldLoader.Load(this, yaml["General"]);
|
||||||
|
|
||||||
|
// TerrainTypes
|
||||||
|
TerrainInfo = yaml["Terrain"].ToDictionary().Values
|
||||||
|
.Select(y => new TerrainTypeInfo(y))
|
||||||
|
.OrderBy(tt => tt.Type)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (TerrainInfo.Length >= byte.MaxValue)
|
||||||
|
throw new YamlException("Too many terrain types.");
|
||||||
|
|
||||||
|
for (byte i = 0; i < TerrainInfo.Length; i++)
|
||||||
|
{
|
||||||
|
var tt = TerrainInfo[i].Type;
|
||||||
|
|
||||||
|
if (terrainIndexByType.ContainsKey(tt))
|
||||||
|
throw new YamlException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
|
||||||
|
|
||||||
|
terrainIndexByType.Add(tt, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultWalkableTerrainIndex = GetTerrainIndex("Clear");
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
Templates = yaml["Templates"].ToDictionary().Values
|
||||||
|
.Select(y => (TerrainTemplateInfo)new DefaultTerrainTemplateInfo(this, y)).ToDictionary(t => t.Id).AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerrainTypeInfo this[byte index]
|
||||||
|
{
|
||||||
|
get { return TerrainInfo[index]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte GetTerrainIndex(string type)
|
||||||
|
{
|
||||||
|
if (terrainIndexByType.TryGetValue(type, out var index))
|
||||||
|
return index;
|
||||||
|
|
||||||
|
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte GetTerrainIndex(TerrainTile r)
|
||||||
|
{
|
||||||
|
var tile = Templates[r.Type][r.Index];
|
||||||
|
if (tile.TerrainType != byte.MaxValue)
|
||||||
|
return tile.TerrainType;
|
||||||
|
|
||||||
|
return defaultWalkableTerrainIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerrainTileInfo GetTileInfo(TerrainTile r)
|
||||||
|
{
|
||||||
|
return Templates[r.Type][r.Index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetTileInfo(TerrainTile r, out TerrainTileInfo info)
|
||||||
|
{
|
||||||
|
if (!Templates.TryGetValue(r.Type, out var tpl) || !tpl.Contains(r.Index))
|
||||||
|
{
|
||||||
|
info = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
info = tpl[r.Index];
|
||||||
|
return info != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ITerrainInfo.Id { get { return Id; } }
|
||||||
|
TerrainTypeInfo[] ITerrainInfo.TerrainTypes { get { return TerrainInfo; } }
|
||||||
|
TerrainTileInfo ITerrainInfo.GetTerrainInfo(TerrainTile r) { return GetTileInfo(r); }
|
||||||
|
bool ITerrainInfo.TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info) { return TryGetTileInfo(r, out info); }
|
||||||
|
Color[] ITerrainInfo.HeightDebugColors { get { return HeightDebugColors; } }
|
||||||
|
IEnumerable<Color> ITerrainInfo.RestrictedPlayerColors { get { return TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); } }
|
||||||
|
float ITerrainInfo.MinHeightColorBrightness { get { return MinHeightColorBrightness; } }
|
||||||
|
float ITerrainInfo.MaxHeightColorBrightness { get { return MaxHeightColorBrightness; } }
|
||||||
|
TerrainTile ITerrainInfo.DefaultTerrainTile { get { return new TerrainTile(Templates.First().Key, 0); } }
|
||||||
|
|
||||||
|
string[] ITemplatedTerrainInfo.EditorTemplateOrder { get { return EditorTemplateOrder; } }
|
||||||
|
IReadOnlyDictionary<ushort, TerrainTemplateInfo> ITemplatedTerrainInfo.Templates { get { return Templates; } }
|
||||||
|
|
||||||
|
void ITerrainInfoNotifyMapCreated.MapCreated(Map map)
|
||||||
|
{
|
||||||
|
// Randomize PickAny tile variants
|
||||||
|
var r = new MersenneTwister();
|
||||||
|
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||||
|
{
|
||||||
|
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||||
|
{
|
||||||
|
var type = map.Tiles[new MPos(i, j)].Type;
|
||||||
|
if (!Templates.TryGetValue(type, out var template) || !template.PickAny)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
map.Tiles[new MPos(i, j)] = new TerrainTile(type, (byte)r.Next(0, template.TilesCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Support;
|
using OpenRA.Support;
|
||||||
|
|
||||||
namespace OpenRA.Graphics
|
namespace OpenRA.Mods.Common.Terrain
|
||||||
{
|
{
|
||||||
class TheaterTemplate
|
public class TheaterTemplate
|
||||||
{
|
{
|
||||||
public readonly Sprite[] Sprites;
|
public readonly Sprite[] Sprites;
|
||||||
public readonly int Stride;
|
public readonly int Stride;
|
||||||
@@ -32,17 +33,15 @@ namespace OpenRA.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Theater : IDisposable
|
public sealed class DefaultTileCache : IDisposable
|
||||||
{
|
{
|
||||||
readonly Dictionary<ushort, TheaterTemplate> templates = new Dictionary<ushort, TheaterTemplate>();
|
readonly Dictionary<ushort, TheaterTemplate> templates = new Dictionary<ushort, TheaterTemplate>();
|
||||||
SheetBuilder sheetBuilder;
|
SheetBuilder sheetBuilder;
|
||||||
readonly Sprite missingTile;
|
readonly Sprite missingTile;
|
||||||
readonly MersenneTwister random;
|
readonly MersenneTwister random;
|
||||||
TileSet tileset;
|
|
||||||
|
|
||||||
public Theater(TileSet tileset, Action<uint, string> onMissingImage = null)
|
public DefaultTileCache(DefaultTerrain terrainInfo, Action<uint, string> onMissingImage = null)
|
||||||
{
|
{
|
||||||
this.tileset = tileset;
|
|
||||||
var allocated = false;
|
var allocated = false;
|
||||||
|
|
||||||
Func<Sheet> allocate = () =>
|
Func<Sheet> allocate = () =>
|
||||||
@@ -51,17 +50,18 @@ namespace OpenRA.Graphics
|
|||||||
throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
|
throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
|
||||||
allocated = true;
|
allocated = true;
|
||||||
|
|
||||||
return new Sheet(SheetType.Indexed, new Size(tileset.SheetSize, tileset.SheetSize));
|
return new Sheet(SheetType.Indexed, new Size(terrainInfo.SheetSize, terrainInfo.SheetSize));
|
||||||
};
|
};
|
||||||
|
|
||||||
random = new MersenneTwister();
|
random = new MersenneTwister();
|
||||||
|
|
||||||
var frameCache = new FrameCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders);
|
var frameCache = new FrameCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders);
|
||||||
foreach (var t in tileset.Templates)
|
foreach (var t in terrainInfo.Templates)
|
||||||
{
|
{
|
||||||
var variants = new List<Sprite[]>();
|
var variants = new List<Sprite[]>();
|
||||||
|
var templateInfo = (DefaultTerrainTemplateInfo)t.Value;
|
||||||
|
|
||||||
foreach (var i in t.Value.Images)
|
foreach (var i in templateInfo.Images)
|
||||||
{
|
{
|
||||||
ISpriteFrame[] allFrames;
|
ISpriteFrame[] allFrames;
|
||||||
if (onMissingImage != null)
|
if (onMissingImage != null)
|
||||||
@@ -79,8 +79,8 @@ namespace OpenRA.Graphics
|
|||||||
else
|
else
|
||||||
allFrames = frameCache[i];
|
allFrames = frameCache[i];
|
||||||
|
|
||||||
var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
|
var frameCount = terrainInfo.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
|
||||||
var indices = t.Value.Frames != null ? t.Value.Frames : Exts.MakeArray(t.Value.TilesCount, j => j);
|
var indices = templateInfo.Frames != null ? templateInfo.Frames : Exts.MakeArray(t.Value.TilesCount, j => j);
|
||||||
|
|
||||||
var start = indices.Min();
|
var start = indices.Min();
|
||||||
var end = indices.Max();
|
var end = indices.Max();
|
||||||
@@ -91,7 +91,7 @@ namespace OpenRA.Graphics
|
|||||||
variants.Add(indices.Select(j =>
|
variants.Add(indices.Select(j =>
|
||||||
{
|
{
|
||||||
var f = allFrames[j];
|
var f = allFrames[j];
|
||||||
var tile = t.Value.Contains(j) ? t.Value[j] : null;
|
var tile = t.Value.Contains(j) ? (DefaultTerrainTileInfo)t.Value[j] : null;
|
||||||
|
|
||||||
// The internal z axis is inverted from expectation (negative is closer)
|
// The internal z axis is inverted from expectation (negative is closer)
|
||||||
var zOffset = tile != null ? -tile.ZOffset : 0;
|
var zOffset = tile != null ? -tile.ZOffset : 0;
|
||||||
@@ -107,13 +107,13 @@ namespace OpenRA.Graphics
|
|||||||
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
|
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
|
||||||
|
|
||||||
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
||||||
Util.FastCopyIntoChannel(s, f.Data, f.Type);
|
OpenRA.Graphics.Util.FastCopyIntoChannel(s, f.Data, f.Type);
|
||||||
|
|
||||||
if (tileset.EnableDepth)
|
if (terrainInfo.EnableDepth)
|
||||||
{
|
{
|
||||||
var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
|
||||||
var depthFrame = allFrames[j + frameCount];
|
var depthFrame = allFrames[j + frameCount];
|
||||||
Util.FastCopyIntoChannel(ss, depthFrame.Data, depthFrame.Type);
|
var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
||||||
|
OpenRA.Graphics.Util.FastCopyIntoChannel(ss, depthFrame.Data, depthFrame.Type);
|
||||||
|
|
||||||
// s and ss are guaranteed to use the same sheet
|
// s and ss are guaranteed to use the same sheet
|
||||||
// because of the custom terrain sheet allocation
|
// because of the custom terrain sheet allocation
|
||||||
@@ -127,13 +127,13 @@ namespace OpenRA.Graphics
|
|||||||
var allSprites = variants.SelectMany(s => s);
|
var allSprites = variants.SelectMany(s => s);
|
||||||
|
|
||||||
// Ignore the offsets baked into R8 sprites
|
// Ignore the offsets baked into R8 sprites
|
||||||
if (tileset.IgnoreTileSpriteOffsets)
|
if (terrainInfo.IgnoreTileSpriteOffsets)
|
||||||
allSprites = allSprites.Select(s => new Sprite(s.Sheet, s.Bounds, s.ZRamp, new float3(float2.Zero, s.Offset.Z), s.Channel, s.BlendMode));
|
allSprites = allSprites.Select(s => new Sprite(s.Sheet, s.Bounds, s.ZRamp, new float3(float2.Zero, s.Offset.Z), s.Channel, s.BlendMode));
|
||||||
|
|
||||||
if (onMissingImage != null && !variants.Any())
|
if (onMissingImage != null && !variants.Any())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
templates.Add(t.Value.Id, new TheaterTemplate(allSprites.ToArray(), variants.First().Count(), t.Value.Images.Length));
|
templates.Add(t.Value.Id, new TheaterTemplate(allSprites.ToArray(), variants.First().Count(), templateInfo.Images.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1x1px transparent tile
|
// 1x1px transparent tile
|
||||||
103
OpenRA.Mods.Common/Terrain/TerrainInfo.cs
Normal file
103
OpenRA.Mods.Common/Terrain/TerrainInfo.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||||
|
* 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
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Terrain
|
||||||
|
{
|
||||||
|
public interface ITemplatedTerrainInfo : ITerrainInfo
|
||||||
|
{
|
||||||
|
string[] EditorTemplateOrder { get; }
|
||||||
|
IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates { 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.ToDictionary()["Tiles"].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 `{0}` template `{1}` defines a frame `{2}` that is not a valid integer.".F(terrainInfo.Id, Id, node.Key));
|
||||||
|
|
||||||
|
if (key < 0 || key >= tileInfo.Length)
|
||||||
|
throw new YamlException("Tileset `{0}` template `{1}` references frame {2}, but only [0..{3}] are valid for a {4}x{5} Size template.".F(terrainInfo.Id, Id, key, tileInfo.Length - 1, Size.X, Size.Y));
|
||||||
|
|
||||||
|
tileInfo[key] = LoadTileInfo(terrainInfo, node.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tileInfo = new TerrainTileInfo[nodes.Count];
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
if (!int.TryParse(node.Key, out var key))
|
||||||
|
throw new YamlException("Tileset `{0}` template `{1}` defines a frame `{2}` that is not a valid integer.".F(terrainInfo.Id, Id, node.Key));
|
||||||
|
|
||||||
|
if (key != i++)
|
||||||
|
throw new YamlException("Tileset `{0}` template `{1}` is missing a definition for frame {2}.".F(terrainInfo.Id, Id, 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("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("MinColor").SetValue(tile, overrideColor);
|
||||||
|
|
||||||
|
if (tile.MaxColor == default)
|
||||||
|
tile.GetType().GetField("MaxColor").SetValue(tile, overrideColor);
|
||||||
|
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerrainTileInfo this[int index] { get { return tileInfo[index]; } }
|
||||||
|
|
||||||
|
public bool Contains(int index)
|
||||||
|
{
|
||||||
|
return index >= 0 && index < tileInfo.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TilesCount
|
||||||
|
{
|
||||||
|
get { return tileInfo.Length; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ using System.Linq;
|
|||||||
using OpenRA.Effects;
|
using OpenRA.Effects;
|
||||||
using OpenRA.GameRules;
|
using OpenRA.GameRules;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
namespace OpenRA.Mods.Common.Traits
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -31,19 +32,20 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
failed = true;
|
failed = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var tileCache = new Theater((TileSet)terrainInfo, onMissingImage);
|
var tileCache = new DefaultTileCache((DefaultTerrain)terrainInfo, onMissingImage);
|
||||||
foreach (var t in terrainInfo.Templates)
|
foreach (var t in terrainInfo.Templates)
|
||||||
{
|
{
|
||||||
for (var v = 0; v < t.Value.Images.Length; v++)
|
var templateInfo = (DefaultTerrainTemplateInfo)t.Value;
|
||||||
|
for (var v = 0; v < templateInfo.Images.Length; v++)
|
||||||
{
|
{
|
||||||
if (!missingImages.Contains(t.Value.Images[v]))
|
if (!missingImages.Contains(templateInfo.Images[v]))
|
||||||
{
|
{
|
||||||
for (var i = 0; i < t.Value.TilesCount; i++)
|
for (var i = 0; i < t.Value.TilesCount; i++)
|
||||||
{
|
{
|
||||||
if (t.Value[i] == null || tileCache.HasTileSprite(new TerrainTile(t.Key, (byte)i), v))
|
if (t.Value[i] == null || tileCache.HasTileSprite(new TerrainTile(t.Key, (byte)i), v))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
onError("\tTemplate `{0}` references frame {1} that does not exist in sprite `{2}`.".F(t.Key, i, t.Value.Images[v]));
|
onError("\tTemplate `{0}` references frame {1} that does not exist in sprite `{2}`.".F(t.Key, i, templateInfo.Images[v]));
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,25 +62,26 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
readonly Map map;
|
readonly Map map;
|
||||||
readonly Dictionary<string, TerrainSpriteLayer> spriteLayers = new Dictionary<string, TerrainSpriteLayer>();
|
readonly Dictionary<string, TerrainSpriteLayer> spriteLayers = new Dictionary<string, TerrainSpriteLayer>();
|
||||||
readonly TileSet terrainInfo;
|
readonly DefaultTerrain terrainInfo;
|
||||||
readonly Theater tileCache;
|
readonly DefaultTileCache tileCache;
|
||||||
bool disposed;
|
bool disposed;
|
||||||
|
|
||||||
public TerrainRenderer(World world)
|
public TerrainRenderer(World world)
|
||||||
{
|
{
|
||||||
map = world.Map;
|
map = world.Map;
|
||||||
terrainInfo = map.Rules.TerrainInfo as TileSet;
|
terrainInfo = map.Rules.TerrainInfo as DefaultTerrain;
|
||||||
if (terrainInfo == null)
|
if (terrainInfo == null)
|
||||||
throw new InvalidDataException("TerrainRenderer can only be used with the default TileSet");
|
throw new InvalidDataException("TerrainRenderer can only be used with the DefaultTerrain parser");
|
||||||
|
|
||||||
tileCache = new Theater(terrainInfo);
|
tileCache = new DefaultTileCache(terrainInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr)
|
void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
foreach (var template in terrainInfo.Templates)
|
foreach (var template in terrainInfo.Templates)
|
||||||
{
|
{
|
||||||
var palette = template.Value.Palette ?? TileSet.TerrainPaletteInternalName;
|
var templateInfo = (DefaultTerrainTemplateInfo)template.Value;
|
||||||
|
var palette = templateInfo.Palette ?? TileSet.TerrainPaletteInternalName;
|
||||||
spriteLayers.GetOrAdd(palette, pal =>
|
spriteLayers.GetOrAdd(palette, pal =>
|
||||||
new TerrainSpriteLayer(world, wr, tileCache.Sheet, BlendMode.Alpha, wr.Palette(palette), world.Type != WorldType.Editor));
|
new TerrainSpriteLayer(world, wr, tileCache.Sheet, BlendMode.Alpha, wr.Palette(palette), world.Type != WorldType.Editor));
|
||||||
}
|
}
|
||||||
@@ -95,7 +98,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
var tile = map.Tiles[cell];
|
var tile = map.Tiles[cell];
|
||||||
var palette = TileSet.TerrainPaletteInternalName;
|
var palette = TileSet.TerrainPaletteInternalName;
|
||||||
if (terrainInfo.Templates.TryGetValue(tile.Type, out var template))
|
if (terrainInfo.Templates.TryGetValue(tile.Type, out var template))
|
||||||
palette = template.Palette ?? palette;
|
palette = ((DefaultTerrainTemplateInfo)template).Palette ?? palette;
|
||||||
|
|
||||||
foreach (var kv in spriteLayers)
|
foreach (var kv in spriteLayers)
|
||||||
kv.Value.Update(cell, palette == kv.Key ? tileCache.TileSprite(tile) : null, false);
|
kv.Value.Update(cell, palette == kv.Key ? tileCache.TileSprite(tile) : null, false);
|
||||||
@@ -159,8 +162,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return templateRect ?? Rectangle.Empty;
|
return templateRect ?? Rectangle.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<IRenderable> ITiledTerrainRenderer.RenderUIPreview(WorldRenderer wr, TerrainTemplateInfo template, int2 origin, float scale)
|
IEnumerable<IRenderable> ITiledTerrainRenderer.RenderUIPreview(WorldRenderer wr, TerrainTemplateInfo t, int2 origin, float scale)
|
||||||
{
|
{
|
||||||
|
var template = t as DefaultTerrainTemplateInfo;
|
||||||
if (template == null)
|
if (template == null)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
@@ -187,8 +191,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<IRenderable> ITiledTerrainRenderer.RenderPreview(WorldRenderer wr, TerrainTemplateInfo template, WPos origin)
|
IEnumerable<IRenderable> ITiledTerrainRenderer.RenderPreview(WorldRenderer wr, TerrainTemplateInfo t, WPos origin)
|
||||||
{
|
{
|
||||||
|
if (!(t is DefaultTerrainTemplateInfo template))
|
||||||
|
yield break;
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
for (var y = 0; y < template.Size.Y; y++)
|
for (var y = 0; y < template.Size.Y; y++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using OpenRA.Activities;
|
|||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Mods.Common.Activities;
|
using OpenRA.Mods.Common.Activities;
|
||||||
using OpenRA.Mods.Common.Graphics;
|
using OpenRA.Mods.Common.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,8 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using OpenRA.Graphics;
|
|
||||||
using OpenRA.Mods.Common.Graphics;
|
using OpenRA.Mods.Common.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.UtilityCommands
|
namespace OpenRA.Mods.Common.UtilityCommands
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using OpenRA.FileSystem;
|
using OpenRA.FileSystem;
|
||||||
using OpenRA.Mods.Common.FileFormats;
|
using OpenRA.Mods.Common.FileFormats;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Widgets;
|
using OpenRA.Widgets;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Traits;
|
|
||||||
using OpenRA.Widgets;
|
using OpenRA.Widgets;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
|
||||||
using OpenRA.Widgets;
|
using OpenRA.Widgets;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Widgets
|
namespace OpenRA.Mods.Common.Widgets
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.Common.Terrain;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
|
|
||||||
namespace OpenRA.Mods.D2k.UtilityCommands
|
namespace OpenRA.Mods.D2k.UtilityCommands
|
||||||
@@ -260,7 +261,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
|||||||
|
|
||||||
Map map;
|
Map map;
|
||||||
Size mapSize;
|
Size mapSize;
|
||||||
ITemplatedTerrainInfo terrainInfo;
|
DefaultTerrain terrainInfo;
|
||||||
List<TerrainTemplateInfo> tileSetsFromYaml;
|
List<TerrainTemplateInfo> tileSetsFromYaml;
|
||||||
int playerCount;
|
int playerCount;
|
||||||
|
|
||||||
@@ -306,7 +307,10 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
|||||||
void Initialize(string mapFile)
|
void Initialize(string mapFile)
|
||||||
{
|
{
|
||||||
mapSize = new Size(stream.ReadUInt16(), stream.ReadUInt16());
|
mapSize = new Size(stream.ReadUInt16(), stream.ReadUInt16());
|
||||||
terrainInfo = (ITemplatedTerrainInfo)Game.ModData.DefaultTerrainInfo["ARRAKIS"];
|
terrainInfo = Game.ModData.DefaultTerrainInfo["ARRAKIS"] as DefaultTerrain;
|
||||||
|
|
||||||
|
if (terrainInfo == null)
|
||||||
|
throw new InvalidDataException("The D2k map importer requires the DefaultTerrain parser.");
|
||||||
|
|
||||||
map = new Map(Game.ModData, terrainInfo, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth)
|
map = new Map(Game.ModData, terrainInfo, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth)
|
||||||
{
|
{
|
||||||
@@ -320,8 +324,11 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
|||||||
|
|
||||||
// Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset
|
// Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset
|
||||||
// Each frame is a tile from the Dune 2000 tileset files, with the Frame ID being the index of the tile in the original file
|
// Each frame is a tile from the Dune 2000 tileset files, with the Frame ID being the index of the tile in the original file
|
||||||
tileSetsFromYaml = terrainInfo.Templates.Where(t => t.Value.Frames != null
|
tileSetsFromYaml = terrainInfo.Templates.Where(t =>
|
||||||
&& t.Value.Images[0].ToLowerInvariant() == tilesetName.ToLowerInvariant()).Select(ts => ts.Value).ToList();
|
{
|
||||||
|
var templateInfo = (DefaultTerrainTemplateInfo)t.Value;
|
||||||
|
return templateInfo.Frames != null && templateInfo.Images[0].ToLowerInvariant() == tilesetName.ToLowerInvariant();
|
||||||
|
}).Select(ts => ts.Value).ToList();
|
||||||
|
|
||||||
var players = new MapPlayers(map.Rules, playerCount);
|
var players = new MapPlayers(map.Rules, playerCount);
|
||||||
map.PlayerDefinitions = players.ToMiniYaml();
|
map.PlayerDefinitions = players.ToMiniYaml();
|
||||||
@@ -461,14 +468,18 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the first tileset template that contains the Frame ID of the original map's tile with the requested index
|
// Get the first tileset template that contains the Frame ID of the original map's tile with the requested index
|
||||||
var template = tileSetsFromYaml.FirstOrDefault(x => x.Frames.Contains(tileIndex));
|
var template = tileSetsFromYaml.FirstOrDefault(x => ((DefaultTerrainTemplateInfo)x).Frames.Contains(tileIndex));
|
||||||
|
|
||||||
// HACK: The arrakis.yaml tileset file seems to be missing some tiles, so just get a replacement for them
|
// HACK: The arrakis.yaml tileset file seems to be missing some tiles, so just get a replacement for them
|
||||||
// Also used for duplicate tiles that are taken from only tileset
|
// Also used for duplicate tiles that are taken from only tileset
|
||||||
if (template == null)
|
if (template == null)
|
||||||
{
|
{
|
||||||
// Just get a template that contains a tile with the same ID as requested
|
// Just get a template that contains a tile with the same ID as requested
|
||||||
var templates = terrainInfo.Templates.Where(t => t.Value.Frames != null && t.Value.Frames.Contains(tileIndex));
|
var templates = terrainInfo.Templates.Where(t =>
|
||||||
|
{
|
||||||
|
var templateInfo = (DefaultTerrainTemplateInfo)t.Value;
|
||||||
|
return templateInfo.Frames != null && templateInfo.Frames.Contains(tileIndex);
|
||||||
|
});
|
||||||
if (templates.Any())
|
if (templates.Any())
|
||||||
template = templates.First().Value;
|
template = templates.First().Value;
|
||||||
}
|
}
|
||||||
@@ -482,7 +493,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
|||||||
}
|
}
|
||||||
|
|
||||||
var templateIndex = template.Id;
|
var templateIndex = template.Id;
|
||||||
var frameIndex = Array.IndexOf(template.Frames, tileIndex);
|
var frameIndex = Array.IndexOf(((DefaultTerrainTemplateInfo)template).Frames, tileIndex);
|
||||||
|
|
||||||
return new TerrainTile(templateIndex, (byte)((frameIndex == -1) ? 0 : frameIndex));
|
return new TerrainTile(templateIndex, (byte)((frameIndex == -1) ? 0 : frameIndex));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user