diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index ef15b56ceb..2f578880cd 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -246,6 +246,7 @@ namespace OpenRA [FieldLoader.Ignore] public Lazy> MapHeight; [FieldLoader.Ignore] public CellLayer CustomTerrain; + [FieldLoader.Ignore] CellLayer cachedTerrainIndexes; [FieldLoader.Ignore] bool initializedCellProjection; [FieldLoader.Ignore] CellLayer cellProjection; @@ -951,9 +952,32 @@ namespace OpenRA public byte GetTerrainIndex(CPos cell) { + const short InvalidCachedTerrainIndex = -1; + + // Lazily initialize a cache for terrain indexes. + if (cachedTerrainIndexes == null) + { + cachedTerrainIndexes = new CellLayer(this); + cachedTerrainIndexes.Clear(InvalidCachedTerrainIndex); + + // Invalidate the entry for a cell if anything could cause the terrain index to change. + Action invalidateTerrainIndex = c => cachedTerrainIndexes[c] = InvalidCachedTerrainIndex; + CustomTerrain.CellEntryChanged += invalidateTerrainIndex; + MapTiles.Value.CellEntryChanged += invalidateTerrainIndex; + } + var uv = cell.ToMPos(this); - var custom = CustomTerrain[uv]; - return custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]); + var terrainIndex = cachedTerrainIndexes[uv]; + + // Cache terrain indexes per cell on demand. + if (terrainIndex == InvalidCachedTerrainIndex) + { + var custom = CustomTerrain[uv]; + terrainIndex = cachedTerrainIndexes[uv] = + custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]); + } + + return (byte)terrainIndex; } public TerrainTypeInfo GetTerrainInfo(CPos cell) diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index 34896f3d36..d60075d369 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -74,7 +74,7 @@ namespace OpenRA public readonly bool PickAny; public readonly string Category; - TerrainTileInfo[] tileInfo; + readonly TerrainTileInfo[] tileInfo; public TerrainTemplateInfo(ushort id, string[] images, int2 size, byte[] tiles) { @@ -177,7 +177,7 @@ namespace OpenRA public readonly bool IgnoreTileSpriteOffsets; [FieldLoader.Ignore] - public readonly Dictionary Templates = new Dictionary(); + public readonly IReadOnlyDictionary Templates; [FieldLoader.Ignore] public readonly TerrainTypeInfo[] TerrainInfo; @@ -217,7 +217,7 @@ namespace OpenRA // Templates Templates = yaml["Templates"].ToDictionary().Values - .Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id); + .Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id).AsReadOnly(); } public TileSet(string name, string id, string palette, TerrainTypeInfo[] terrainInfo)