Add height map support.
This commit is contained in:
@@ -24,6 +24,38 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA
|
namespace OpenRA
|
||||||
{
|
{
|
||||||
|
struct BinaryDataHeader
|
||||||
|
{
|
||||||
|
public readonly byte Format;
|
||||||
|
public readonly uint TilesOffset;
|
||||||
|
public readonly uint HeightsOffset;
|
||||||
|
public readonly uint ResourcesOffset;
|
||||||
|
|
||||||
|
public BinaryDataHeader(Stream s, int2 expectedSize)
|
||||||
|
{
|
||||||
|
Format = s.ReadUInt8();
|
||||||
|
var width = s.ReadUInt16();
|
||||||
|
var height = s.ReadUInt16();
|
||||||
|
if (width != expectedSize.X || height != expectedSize.Y)
|
||||||
|
throw new InvalidDataException("Invalid tile data");
|
||||||
|
|
||||||
|
if (Format == 1)
|
||||||
|
{
|
||||||
|
TilesOffset = 5;
|
||||||
|
HeightsOffset = 0;
|
||||||
|
ResourcesOffset = (uint)(3 * width * height + 5);
|
||||||
|
}
|
||||||
|
else if (Format == 2)
|
||||||
|
{
|
||||||
|
TilesOffset = s.ReadUInt32();
|
||||||
|
HeightsOffset = s.ReadUInt32();
|
||||||
|
ResourcesOffset = s.ReadUInt32();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new InvalidDataException("Unknown binary map format '{0}'".F(Format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class MapOptions
|
public class MapOptions
|
||||||
{
|
{
|
||||||
public bool? Cheats;
|
public bool? Cheats;
|
||||||
@@ -118,11 +150,14 @@ namespace OpenRA
|
|||||||
[FieldLoader.Ignore] public List<MiniYamlNode> TranslationDefinitions = new List<MiniYamlNode>();
|
[FieldLoader.Ignore] public List<MiniYamlNode> TranslationDefinitions = new List<MiniYamlNode>();
|
||||||
|
|
||||||
// Binary map data
|
// Binary map data
|
||||||
[FieldLoader.Ignore] public byte TileFormat = 1;
|
[FieldLoader.Ignore] public byte TileFormat = 2;
|
||||||
|
|
||||||
public int2 MapSize;
|
public int2 MapSize;
|
||||||
|
|
||||||
[FieldLoader.Ignore] public Lazy<CellLayer<TerrainTile>> MapTiles;
|
[FieldLoader.Ignore] public Lazy<CellLayer<TerrainTile>> MapTiles;
|
||||||
[FieldLoader.Ignore] public Lazy<CellLayer<ResourceTile>> MapResources;
|
[FieldLoader.Ignore] public Lazy<CellLayer<ResourceTile>> MapResources;
|
||||||
|
[FieldLoader.Ignore] public Lazy<CellLayer<byte>> MapHeight;
|
||||||
|
|
||||||
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
|
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
|
||||||
|
|
||||||
[FieldLoader.Ignore] Lazy<TileSet> cachedTileSet;
|
[FieldLoader.Ignore] Lazy<TileSet> cachedTileSet;
|
||||||
@@ -145,6 +180,13 @@ namespace OpenRA
|
|||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var makeMapHeight = Exts.Lazy(() =>
|
||||||
|
{
|
||||||
|
var ret = new CellLayer<byte>(tileShape, size);
|
||||||
|
ret.Clear(0);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
var map = new Map()
|
var map = new Map()
|
||||||
{
|
{
|
||||||
Title = "Name your map here",
|
Title = "Name your map here",
|
||||||
@@ -155,6 +197,7 @@ namespace OpenRA
|
|||||||
Options = new MapOptions(),
|
Options = new MapOptions(),
|
||||||
MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(tileShape, size)),
|
MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(tileShape, size)),
|
||||||
MapTiles = makeMapTiles,
|
MapTiles = makeMapTiles,
|
||||||
|
MapHeight = makeMapHeight,
|
||||||
Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
|
Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
|
||||||
Smudges = Exts.Lazy(() => new List<SmudgeReference>())
|
Smudges = Exts.Lazy(() => new List<SmudgeReference>())
|
||||||
};
|
};
|
||||||
@@ -253,6 +296,8 @@ namespace OpenRA
|
|||||||
|
|
||||||
MapTiles = Exts.Lazy(() => LoadMapTiles());
|
MapTiles = Exts.Lazy(() => LoadMapTiles());
|
||||||
MapResources = Exts.Lazy(() => LoadResourceTiles());
|
MapResources = Exts.Lazy(() => LoadResourceTiles());
|
||||||
|
MapHeight = Exts.Lazy(() => LoadMapHeight());
|
||||||
|
|
||||||
TileShape = Game.modData.Manifest.TileShape;
|
TileShape = Game.modData.Manifest.TileShape;
|
||||||
SubCellOffsets = Game.modData.Manifest.SubCellOffsets;
|
SubCellOffsets = Game.modData.Manifest.SubCellOffsets;
|
||||||
LastSubCell = (SubCell)(SubCellOffsets.Length - 1);
|
LastSubCell = (SubCell)(SubCellOffsets.Length - 1);
|
||||||
@@ -395,29 +440,20 @@ namespace OpenRA
|
|||||||
public CellLayer<TerrainTile> LoadMapTiles()
|
public CellLayer<TerrainTile> LoadMapTiles()
|
||||||
{
|
{
|
||||||
var tiles = new CellLayer<TerrainTile>(this);
|
var tiles = new CellLayer<TerrainTile>(this);
|
||||||
using (var dataStream = Container.GetContent("map.bin"))
|
using (var s = Container.GetContent("map.bin"))
|
||||||
{
|
{
|
||||||
if (dataStream.ReadUInt8() != 1)
|
var header = new BinaryDataHeader(s, MapSize);
|
||||||
throw new InvalidDataException("Unknown binary map format");
|
if (header.TilesOffset > 0)
|
||||||
|
{
|
||||||
// Load header info
|
s.Position = header.TilesOffset;
|
||||||
var width = dataStream.ReadUInt16();
|
|
||||||
var height = dataStream.ReadUInt16();
|
|
||||||
|
|
||||||
if (width != MapSize.X || height != MapSize.Y)
|
|
||||||
throw new InvalidDataException("Invalid tile data");
|
|
||||||
|
|
||||||
// Load tile data
|
|
||||||
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3);
|
|
||||||
var d = 0;
|
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
{
|
{
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
{
|
{
|
||||||
var tile = BitConverter.ToUInt16(data, d);
|
var tile = s.ReadUInt16();
|
||||||
d += 2;
|
var index = s.ReadUInt8();
|
||||||
|
|
||||||
var index = data[d++];
|
// TODO: Remember to remove this when rewriting tile variants / PickAny
|
||||||
if (index == byte.MaxValue)
|
if (index == byte.MaxValue)
|
||||||
index = (byte)(i % 4 + (j % 4) * 4);
|
index = (byte)(i % 4 + (j % 4) * 4);
|
||||||
|
|
||||||
@@ -425,6 +461,26 @@ namespace OpenRA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellLayer<byte> LoadMapHeight()
|
||||||
|
{
|
||||||
|
var maxHeight = cachedTileSet.Value.MaxGroundHeight;
|
||||||
|
var tiles = new CellLayer<byte>(this);
|
||||||
|
using (var s = Container.GetContent("map.bin"))
|
||||||
|
{
|
||||||
|
var header = new BinaryDataHeader(s, MapSize);
|
||||||
|
if (header.HeightsOffset > 0)
|
||||||
|
{
|
||||||
|
s.Position = header.HeightsOffset;
|
||||||
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
|
tiles[i, j] = s.ReadUInt8().Clamp((byte)0, maxHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
@@ -433,28 +489,22 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
var resources = new CellLayer<ResourceTile>(this);
|
var resources = new CellLayer<ResourceTile>(this);
|
||||||
|
|
||||||
using (var dataStream = Container.GetContent("map.bin"))
|
using (var s = Container.GetContent("map.bin"))
|
||||||
{
|
{
|
||||||
if (dataStream.ReadUInt8() != 1)
|
var header = new BinaryDataHeader(s, MapSize);
|
||||||
throw new InvalidDataException("Unknown binary map format");
|
if (header.ResourcesOffset > 0)
|
||||||
|
{
|
||||||
// Load header info
|
s.Position = header.ResourcesOffset;
|
||||||
var width = dataStream.ReadUInt16();
|
|
||||||
var height = dataStream.ReadUInt16();
|
|
||||||
|
|
||||||
if (width != MapSize.X || height != MapSize.Y)
|
|
||||||
throw new InvalidDataException("Invalid tile data");
|
|
||||||
|
|
||||||
// Skip past tile data
|
|
||||||
dataStream.Seek(3 * MapSize.X * MapSize.Y, SeekOrigin.Current);
|
|
||||||
|
|
||||||
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2);
|
|
||||||
var d = 0;
|
|
||||||
|
|
||||||
// Load resource data
|
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
|
{
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
resources[i, j] = new ResourceTile(data[d++], data[d++]);
|
{
|
||||||
|
var type = s.ReadUInt8();
|
||||||
|
var density = s.ReadUInt8();
|
||||||
|
resources[i, j] = new ResourceTile(type, density);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resources;
|
return resources;
|
||||||
@@ -465,21 +515,45 @@ namespace OpenRA
|
|||||||
var dataStream = new MemoryStream();
|
var dataStream = new MemoryStream();
|
||||||
using (var writer = new BinaryWriter(dataStream))
|
using (var writer = new BinaryWriter(dataStream))
|
||||||
{
|
{
|
||||||
// File header consists of a version byte, followed by 2 ushorts for width and height
|
// Binary data version
|
||||||
writer.Write(TileFormat);
|
writer.Write(TileFormat);
|
||||||
|
|
||||||
|
// Size
|
||||||
writer.Write((ushort)MapSize.X);
|
writer.Write((ushort)MapSize.X);
|
||||||
writer.Write((ushort)MapSize.Y);
|
writer.Write((ushort)MapSize.Y);
|
||||||
|
|
||||||
|
// Data offsets
|
||||||
|
var tilesOffset = 17;
|
||||||
|
var heightsOffset = cachedTileSet.Value.MaxGroundHeight > 0 ? 3 * MapSize.X * MapSize.Y + 17 : 0;
|
||||||
|
var resourcesOffset = (cachedTileSet.Value.MaxGroundHeight > 0 ? 4 : 3) * MapSize.X * MapSize.Y + 17;
|
||||||
|
|
||||||
|
writer.Write((uint)tilesOffset);
|
||||||
|
writer.Write((uint)heightsOffset);
|
||||||
|
writer.Write((uint)resourcesOffset);
|
||||||
|
|
||||||
// Tile data
|
// Tile data
|
||||||
|
if (tilesOffset != 0)
|
||||||
|
{
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
|
{
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
{
|
{
|
||||||
var tile = MapTiles.Value[i, j];
|
var tile = MapTiles.Value[i, j];
|
||||||
writer.Write(tile.Type);
|
writer.Write(tile.Type);
|
||||||
writer.Write(tile.Index);
|
writer.Write(tile.Index);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height data
|
||||||
|
if (heightsOffset != 0)
|
||||||
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
|
writer.Write(MapHeight.Value[i, j]);
|
||||||
|
|
||||||
// Resource data
|
// Resource data
|
||||||
|
if (resourcesOffset != 0)
|
||||||
|
{
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
{
|
{
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
@@ -490,6 +564,7 @@ namespace OpenRA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return dataStream.ToArray();
|
return dataStream.ToArray();
|
||||||
}
|
}
|
||||||
@@ -513,7 +588,9 @@ namespace OpenRA
|
|||||||
// (b) Therefore:
|
// (b) Therefore:
|
||||||
// - ax + by adds (a - b) * 512 + 512 to u
|
// - ax + by adds (a - b) * 512 + 512 to u
|
||||||
// - ax + by adds (a + b) * 512 + 512 to v
|
// - ax + by adds (a + b) * 512 + 512 to v
|
||||||
return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), 0);
|
|
||||||
|
var z = Contains(cell) ? 512 * MapHeight.Value[cell] : 0;
|
||||||
|
return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WPos CenterOfSubCell(CPos cell, SubCell subCell)
|
public WPos CenterOfSubCell(CPos cell, SubCell subCell)
|
||||||
@@ -588,10 +665,12 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
var oldMapTiles = MapTiles.Value;
|
var oldMapTiles = MapTiles.Value;
|
||||||
var oldMapResources = MapResources.Value;
|
var oldMapResources = MapResources.Value;
|
||||||
|
var oldMapHeight = MapHeight.Value;
|
||||||
var newSize = new Size(width, height);
|
var newSize = new Size(width, height);
|
||||||
|
|
||||||
MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0]));
|
MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0]));
|
||||||
MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0]));
|
MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0]));
|
||||||
|
MapHeight = Exts.Lazy(() => CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[0, 0]));
|
||||||
MapSize = new int2(newSize);
|
MapSize = new int2(newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
public class TileSet
|
public class TileSet
|
||||||
{
|
{
|
||||||
static readonly string[] Fields = { "Name", "Id", "SheetSize", "Palette", "PlayerPalette", "Extensions", "WaterPaletteRotationBase", "EditorTemplateOrder", "IgnoreTileSpriteOffsets" };
|
static readonly string[] Fields = { "Name", "Id", "SheetSize", "Palette", "PlayerPalette", "Extensions", "WaterPaletteRotationBase", "EditorTemplateOrder", "IgnoreTileSpriteOffsets", "MaximumHeight" };
|
||||||
|
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
public readonly string Id;
|
public readonly string Id;
|
||||||
@@ -157,10 +157,12 @@ namespace OpenRA
|
|||||||
public readonly string PlayerPalette;
|
public readonly string PlayerPalette;
|
||||||
public readonly string[] Extensions;
|
public readonly string[] Extensions;
|
||||||
public readonly int WaterPaletteRotationBase = 0x60;
|
public readonly int WaterPaletteRotationBase = 0x60;
|
||||||
public readonly Dictionary<ushort, TerrainTemplateInfo> Templates = new Dictionary<ushort, TerrainTemplateInfo>();
|
public readonly byte MaxGroundHeight = 0;
|
||||||
public readonly string[] EditorTemplateOrder;
|
public readonly string[] EditorTemplateOrder;
|
||||||
public readonly bool IgnoreTileSpriteOffsets;
|
public readonly bool IgnoreTileSpriteOffsets;
|
||||||
|
|
||||||
|
public readonly Dictionary<ushort, TerrainTemplateInfo> Templates = new Dictionary<ushort, TerrainTemplateInfo>();
|
||||||
|
|
||||||
public readonly TerrainTypeInfo[] TerrainInfo;
|
public readonly TerrainTypeInfo[] TerrainInfo;
|
||||||
readonly Dictionary<string, byte> terrainIndexByType = new Dictionary<string, byte>();
|
readonly Dictionary<string, byte> terrainIndexByType = new Dictionary<string, byte>();
|
||||||
readonly byte defaultWalkableTerrainIndex;
|
readonly byte defaultWalkableTerrainIndex;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ General:
|
|||||||
Id: TEMPERAT
|
Id: TEMPERAT
|
||||||
Extensions: .tem, .shp
|
Extensions: .tem, .shp
|
||||||
Palette: isotem.pal
|
Palette: isotem.pal
|
||||||
|
MaxGroundHeight: 16
|
||||||
EditorTemplateOrder: Misc Buildings, Clear, Cliff Pieces, Ice Flow, House, Blank, Ice Ramps, Cliff Set, Civilian Buildings, Shore Pieces, Rough LAT tile, Clear/Rough LAT, Cliff/Water pieces, Bendy Dirt Roads, Dirt Road Junctions, Straight Dirt Roads, Bridges, Paved Roads, Water, Dirt Road Slopes, Slope Set Pieces, Dead Oil Tanker, Ruins, Waterfalls, Ground 01, Ground 02, Sand, Sand/Clear LAT, Rough ground, Paved Road Ends, TrainBridges, Pavement, Pavement/Clear LAT, Paved road bits, Green, Green/Clear LAT, Ramp edge fixup, Water slopes, Pavement (Use for LAT), Paved Road Slopes, Monorail Slopes, Waterfalls-B, Waterfalls-C, Waterfalls-D, Tunnel Floor, Tunnel Side, TrackTunnel Floor, Destroyable Cliffs, Water Caves, Scrin Wreckage, DirtTrackTunnel Floor, DirtTunnel Floor
|
EditorTemplateOrder: Misc Buildings, Clear, Cliff Pieces, Ice Flow, House, Blank, Ice Ramps, Cliff Set, Civilian Buildings, Shore Pieces, Rough LAT tile, Clear/Rough LAT, Cliff/Water pieces, Bendy Dirt Roads, Dirt Road Junctions, Straight Dirt Roads, Bridges, Paved Roads, Water, Dirt Road Slopes, Slope Set Pieces, Dead Oil Tanker, Ruins, Waterfalls, Ground 01, Ground 02, Sand, Sand/Clear LAT, Rough ground, Paved Road Ends, TrainBridges, Pavement, Pavement/Clear LAT, Paved road bits, Green, Green/Clear LAT, Ramp edge fixup, Water slopes, Pavement (Use for LAT), Paved Road Slopes, Monorail Slopes, Waterfalls-B, Waterfalls-C, Waterfalls-D, Tunnel Floor, Tunnel Side, TrackTunnel Floor, Destroyable Cliffs, Water Caves, Scrin Wreckage, DirtTrackTunnel Floor, DirtTunnel Floor
|
||||||
SheetSize: 2048
|
SheetSize: 2048
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user