Moved RotationBase from tileset definition to WaterPaletteRotation effect. Unhardcoded rotation range and rotation rate. Added possibility to exclude Tilesets from effect. Fixed RA water palette rotation for actors on desert maps (#8872).
302 lines
8.3 KiB
C#
302 lines
8.3 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2015 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. For more information,
|
|
* see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using OpenRA.Primitives;
|
|
|
|
namespace OpenRA
|
|
{
|
|
public class TerrainTileInfo
|
|
{
|
|
[FieldLoader.Ignore]
|
|
public readonly byte TerrainType = byte.MaxValue;
|
|
public readonly byte Height;
|
|
public readonly byte RampType;
|
|
public readonly Color LeftColor;
|
|
public readonly Color RightColor;
|
|
|
|
public MiniYaml Save(TileSet tileSet)
|
|
{
|
|
var root = new List<MiniYamlNode>();
|
|
if (Height != 0)
|
|
root.Add(FieldSaver.SaveField(this, "Height"));
|
|
|
|
if (RampType != 0)
|
|
root.Add(FieldSaver.SaveField(this, "RampType"));
|
|
|
|
if (LeftColor != tileSet.TerrainInfo[TerrainType].Color)
|
|
root.Add(FieldSaver.SaveField(this, "LeftColor"));
|
|
|
|
if (RightColor != tileSet.TerrainInfo[TerrainType].Color)
|
|
root.Add(FieldSaver.SaveField(this, "RightColor"));
|
|
|
|
return new MiniYaml(tileSet.TerrainInfo[TerrainType].Type, root);
|
|
}
|
|
}
|
|
|
|
public class TerrainTypeInfo
|
|
{
|
|
static readonly TerrainTypeInfo Default = new TerrainTypeInfo();
|
|
|
|
public readonly string Type;
|
|
public readonly HashSet<string> TargetTypes = new HashSet<string>();
|
|
public readonly HashSet<string> AcceptsSmudgeType = new HashSet<string>();
|
|
public readonly bool IsWater = false; // TODO: Remove this
|
|
public readonly Color Color;
|
|
public readonly string CustomCursor;
|
|
|
|
// Private default ctor for serialization comparison
|
|
TerrainTypeInfo() { }
|
|
|
|
public TerrainTypeInfo(MiniYaml my) { FieldLoader.Load(this, my); }
|
|
|
|
public MiniYaml Save() { return FieldSaver.SaveDifferences(this, Default); }
|
|
}
|
|
|
|
public class TerrainTemplateInfo
|
|
{
|
|
static readonly TerrainTemplateInfo Default = new TerrainTemplateInfo(0, new string[] { null }, int2.Zero, null);
|
|
|
|
public readonly ushort Id;
|
|
public readonly string[] Images;
|
|
public readonly int[] Frames;
|
|
public readonly int2 Size;
|
|
public readonly bool PickAny;
|
|
public readonly string Category;
|
|
|
|
readonly TerrainTileInfo[] tileInfo;
|
|
|
|
public TerrainTemplateInfo(ushort id, string[] images, int2 size, byte[] tiles)
|
|
{
|
|
this.Id = id;
|
|
this.Images = images;
|
|
this.Size = size;
|
|
}
|
|
|
|
public TerrainTemplateInfo(TileSet tileSet, 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)
|
|
{
|
|
int key;
|
|
if (!int.TryParse(node.Key, out key) || key < 0 || key >= tileInfo.Length)
|
|
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
|
|
|
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tileInfo = new TerrainTileInfo[nodes.Count];
|
|
|
|
var i = 0;
|
|
foreach (var node in nodes)
|
|
{
|
|
int key;
|
|
if (!int.TryParse(node.Key, out key) || key != i++)
|
|
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
|
|
|
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static TerrainTileInfo LoadTileInfo(TileSet tileSet, 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, tileSet.GetTerrainIndex(my.Value));
|
|
|
|
// Fall back to the terrain-type color if necessary
|
|
var overrideColor = tileSet.TerrainInfo[tile.TerrainType].Color;
|
|
if (tile.LeftColor == default(Color))
|
|
tile.GetType().GetField("LeftColor").SetValue(tile, overrideColor);
|
|
|
|
if (tile.RightColor == default(Color))
|
|
tile.GetType().GetField("RightColor").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 MiniYaml Save(TileSet tileSet)
|
|
{
|
|
var root = FieldSaver.SaveDifferences(this, Default);
|
|
|
|
var tileYaml = tileInfo
|
|
.Select((ti, i) => Pair.New(i.ToString(), ti))
|
|
.Where(t => t.Second != null)
|
|
.Select(t => new MiniYamlNode(t.First, t.Second.Save(tileSet)))
|
|
.ToList();
|
|
|
|
root.Nodes.Add(new MiniYamlNode("Tiles", null, tileYaml));
|
|
|
|
return root;
|
|
}
|
|
}
|
|
|
|
public class TileSet
|
|
{
|
|
public readonly string Name;
|
|
public readonly string Id;
|
|
public readonly int SheetSize = 512;
|
|
public readonly string Palette;
|
|
public readonly string PlayerPalette;
|
|
public readonly Color[] HeightDebugColors = new[] { Color.Red };
|
|
public readonly string[] EditorTemplateOrder;
|
|
public readonly bool IgnoreTileSpriteOffsets;
|
|
|
|
[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;
|
|
|
|
// Private default ctor for serialization comparison
|
|
TileSet() { }
|
|
|
|
public TileSet(ModData modData, string filepath)
|
|
{
|
|
var yaml = MiniYaml.DictFromFile(filepath);
|
|
|
|
// 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 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}' 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, string palette, TerrainTypeInfo[] terrainInfo)
|
|
{
|
|
Name = name;
|
|
Id = id;
|
|
Palette = palette;
|
|
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)
|
|
{
|
|
byte index;
|
|
if (terrainIndexByType.TryGetValue(type, out index))
|
|
return index;
|
|
|
|
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
|
}
|
|
|
|
public byte GetTerrainIndex(TerrainTile r)
|
|
{
|
|
TerrainTemplateInfo tpl;
|
|
if (!Templates.TryGetValue(r.Type, out tpl))
|
|
return defaultWalkableTerrainIndex;
|
|
|
|
if (tpl.Contains(r.Index))
|
|
{
|
|
var tile = tpl[r.Index];
|
|
if (tile != null && tile.TerrainType != byte.MaxValue)
|
|
return tile.TerrainType;
|
|
}
|
|
|
|
return defaultWalkableTerrainIndex;
|
|
}
|
|
|
|
public TerrainTileInfo GetTileInfo(TerrainTile r)
|
|
{
|
|
TerrainTemplateInfo tpl;
|
|
if (!Templates.TryGetValue(r.Type, out tpl))
|
|
return null;
|
|
|
|
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
|
|
}
|
|
|
|
public void Save(string filepath)
|
|
{
|
|
var root = new List<MiniYamlNode>();
|
|
root.Add(new MiniYamlNode("General", FieldSaver.SaveDifferences(this, new TileSet())));
|
|
|
|
root.Add(new MiniYamlNode("Terrain", null,
|
|
TerrainInfo.Select(t => new MiniYamlNode("TerrainType@{0}".F(t.Type), t.Save())).ToList()));
|
|
|
|
root.Add(new MiniYamlNode("Templates", null,
|
|
Templates.Select(t => new MiniYamlNode("Template@{0}".F(t.Value.Id), t.Value.Save(this))).ToList()));
|
|
root.WriteToFile(filepath);
|
|
}
|
|
}
|
|
}
|