diff --git a/OpenRA.Editor/BrushTool.cs b/OpenRA.Editor/BrushTool.cs index 220fdacb56..e41fe45171 100644 --- a/OpenRA.Editor/BrushTool.cs +++ b/OpenRA.Editor/BrushTool.cs @@ -37,16 +37,15 @@ namespace OpenRA.Editor for (var u = 0; u < template.Size.X; u++) for (var v = 0; v < template.Size.Y; v++) { - if (surface.Map.IsInMap(new CVec(u, v) + pos)) + var cell = pos + new CVec(u, v); + if (surface.Map.Contains(cell)) { var z = u + v * template.Size.X; if (tile[z].Length > 0) - surface.Map.MapTiles.Value[u + pos.X, v + pos.Y] = - new TileReference - { - Type = brushTemplate.N, - Index = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z, - }; + { + var index = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z; + surface.Map.MapTiles.Value[cell] = new TerrainTile(brushTemplate.N, index); + } var ch = new int2((pos.X + u) / Surface.ChunkSize, (pos.Y + v) / Surface.ChunkSize); if (surface.Chunks.ContainsKey(ch)) @@ -70,14 +69,15 @@ namespace OpenRA.Editor void FloodFillWithBrush(Surface s, CPos pos) { var queue = new Queue(); - var replace = s.Map.MapTiles.Value[pos.X, pos.Y]; + var replace = s.Map.MapTiles.Value[pos]; var touched = new bool[s.Map.MapSize.X, s.Map.MapSize.Y]; Action maybeEnqueue = (x, y) => { - if (s.Map.IsInMap(x, y) && !touched[x, y]) + var c = new CPos(x, y); + if (s.Map.Contains(c) && !touched[x, y]) { - queue.Enqueue(new CPos(x, y)); + queue.Enqueue(c); touched[x, y] = true; } }; @@ -86,7 +86,7 @@ namespace OpenRA.Editor while (queue.Count > 0) { var p = queue.Dequeue(); - if (s.Map.MapTiles.Value[p.X, p.Y].Type != replace.Type) + if (s.Map.MapTiles.Value[p].Type != replace.Type) continue; var a = FindEdge(s, p, new CVec(-1, 0), replace); @@ -94,10 +94,10 @@ namespace OpenRA.Editor for (var x = a.X; x <= b.X; x++) { - s.Map.MapTiles.Value[x, p.Y] = new TileReference { Type = brushTemplate.N, Index = (byte)0 }; - if (s.Map.MapTiles.Value[x, p.Y - 1].Type == replace.Type) + s.Map.MapTiles.Value[new CPos(x, p.Y)] = new TerrainTile(brushTemplate.N, (byte)0); + if (s.Map.MapTiles.Value[new CPos(x, p.Y - 1)].Type == replace.Type) maybeEnqueue(x, p.Y - 1); - if (s.Map.MapTiles.Value[x, p.Y + 1].Type == replace.Type) + if (s.Map.MapTiles.Value[new CPos(x, p.Y + 1)].Type == replace.Type) maybeEnqueue(x, p.Y + 1); } } @@ -107,13 +107,13 @@ namespace OpenRA.Editor s.Chunks.Clear(); } - static CPos FindEdge(Surface s, CPos p, CVec d, TileReference replace) + static CPos FindEdge(Surface s, CPos p, CVec d, TerrainTile replace) { for (;;) { var q = p + d; - if (!s.Map.IsInMap(q)) return p; - if (s.Map.MapTiles.Value[q.X, q.Y].Type != replace.Type) return p; + if (!s.Map.Contains(q)) return p; + if (s.Map.MapTiles.Value[q].Type != replace.Type) return p; p = q; } } diff --git a/OpenRA.Editor/Form1.cs b/OpenRA.Editor/Form1.cs index 51d7c68441..c108f0b690 100644 --- a/OpenRA.Editor/Form1.cs +++ b/OpenRA.Editor/Form1.cs @@ -641,7 +641,8 @@ namespace OpenRA.Editor for (var i = 0; i < surface1.Map.MapSize.X; i++) for (var j = 0; j < surface1.Map.MapSize.Y; j++) { - if (surface1.Map.MapResources.Value[i, j].Type != 0) + var cell = new CPos(i, j); + if (surface1.Map.MapResources.Value[cell].Type != 0) totalResource += GetResourceValue(i, j); } @@ -654,9 +655,12 @@ namespace OpenRA.Editor for (var u = -1; u < 2; u++) for (var v = -1; v < 2; v++) { - if (!surface1.Map.IsInMap(new CPos(x + u, y + v))) + var cell = new CPos(x + u, y + v); + + if (!surface1.Map.Contains(cell)) continue; - if (surface1.Map.MapResources.Value[x + u, y + v].Type == resourceType) + + if (surface1.Map.MapResources.Value[cell].Type == resourceType) ++sum; } @@ -666,7 +670,7 @@ namespace OpenRA.Editor int GetResourceValue(int x, int y) { var imageLength = 0; - int type = surface1.Map.MapResources.Value[x, y].Type; + var type = surface1.Map.MapResources.Value[new CPos(x, y)].Type; var template = surface1.ResourceTemplates.FirstOrDefault(a => a.Value.Info.ResourceType == type).Value; if (type == 1) imageLength = 12; diff --git a/OpenRA.Editor/ResourceTool.cs b/OpenRA.Editor/ResourceTool.cs index f8f6351e05..b135daaf0d 100644 --- a/OpenRA.Editor/ResourceTool.cs +++ b/OpenRA.Editor/ResourceTool.cs @@ -21,12 +21,9 @@ namespace OpenRA.Editor public void Apply(Surface surface) { - surface.Map.MapResources.Value[surface.GetBrushLocation().X, surface.GetBrushLocation().Y] - = new TileReference - { - Type = (byte)resourceTemplate.Info.ResourceType, - Index = (byte)random.Next(resourceTemplate.Info.MaxDensity) - }; + var type = (byte)resourceTemplate.Info.ResourceType; + var index = (byte)random.Next(resourceTemplate.Info.MaxDensity); + surface.Map.MapResources.Value[surface.GetBrushLocation()] = new ResourceTile(type, index); var ch = new int2(surface.GetBrushLocation().X / Surface.ChunkSize, surface.GetBrushLocation().Y / Surface.ChunkSize); diff --git a/OpenRA.Editor/Surface.cs b/OpenRA.Editor/Surface.cs index 4d4fb25892..16fcb1f678 100644 --- a/OpenRA.Editor/Surface.cs +++ b/OpenRA.Editor/Surface.cs @@ -62,8 +62,8 @@ namespace OpenRA.Editor public bool ShowRuler; public bool IsPaste { get { return TileSelection != null && ResourceSelection != null; } } - public TileReference[,] TileSelection; - public TileReference[,] ResourceSelection; + public TerrainTile[,] TileSelection; + public ResourceTile[,] ResourceSelection; public CPos SelectionStart; public CPos SelectionEnd; @@ -206,9 +206,9 @@ namespace OpenRA.Editor var key = Map.Actors.Value.FirstOrDefault(a => a.Value.Location() == brushLocation); if (key.Key != null) Map.Actors.Value.Remove(key.Key); - if (Map.MapResources.Value[brushLocation.X, brushLocation.Y].Type != 0) + if (Map.MapResources.Value[brushLocation].Type != 0) { - Map.MapResources.Value[brushLocation.X, brushLocation.Y] = new TileReference(); + Map.MapResources.Value[brushLocation] = new ResourceTile(0, 0); var ch = new int2(brushLocation.X / ChunkSize, brushLocation.Y / ChunkSize); if (Chunks.ContainsKey(ch)) { @@ -271,7 +271,8 @@ namespace OpenRA.Editor for (var i = 0; i < ChunkSize; i++) for (var j = 0; j < ChunkSize; j++) { - var tr = Map.MapTiles.Value[u * ChunkSize + i, v * ChunkSize + j]; + var cell = new CPos(u * ChunkSize + i, v * ChunkSize + j); + var tr = Map.MapTiles.Value[cell]; var tile = TileSetRenderer.Data(tr.Type); var index = (tr.Index < tile.Count) ? tr.Index : (byte)0; var rawImage = tile[index]; @@ -279,9 +280,9 @@ namespace OpenRA.Editor for (var y = 0; y < TileSetRenderer.TileSize; y++) p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = Palette.GetColor(rawImage[x + TileSetRenderer.TileSize * y]).ToArgb(); - if (Map.MapResources.Value[u * ChunkSize + i, v * ChunkSize + j].Type != 0) + if (Map.MapResources.Value[cell].Type != 0) { - var resourceImage = ResourceTemplates[Map.MapResources.Value[u * ChunkSize + i, v * ChunkSize + j].Type].Bitmap; + var resourceImage = ResourceTemplates[Map.MapResources.Value[cell].Type].Bitmap; var srcdata = resourceImage.LockBits(resourceImage.Bounds(), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); @@ -508,16 +509,17 @@ namespace OpenRA.Editor var width = Math.Abs((start - end).X); var height = Math.Abs((start - end).Y); - TileSelection = new TileReference[width, height]; - ResourceSelection = new TileReference[width, height]; + TileSelection = new TerrainTile[width, height]; + ResourceSelection = new ResourceTile[width, height]; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { // TODO: crash prevention - TileSelection[x, y] = Map.MapTiles.Value[start.X + x, start.Y + y]; - ResourceSelection[x, y] = Map.MapResources.Value[start.X + x, start.Y + y]; + var cell = new CPos(start.X + x, start.Y + y); + TileSelection[x, y] = Map.MapTiles.Value[cell]; + ResourceSelection[x, y] = Map.MapResources.Value[cell]; } } } @@ -534,10 +536,11 @@ namespace OpenRA.Editor { var mapX = loc.X + x; var mapY = loc.Y + y; + var cell = new CPos(mapX, mapY); // TODO: crash prevention for outside of bounds - Map.MapTiles.Value[mapX, mapY] = TileSelection[x, y]; - Map.MapResources.Value[mapX, mapY] = ResourceSelection[x, y]; + Map.MapTiles.Value[cell] = TileSelection[x, y]; + Map.MapResources.Value[cell] = ResourceSelection[x, y]; var ch = new int2(mapX / ChunkSize, mapY / ChunkSize); if (Chunks.ContainsKey(ch)) diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index b554a2271e..ef7bb5bbfc 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -180,7 +180,7 @@ namespace OpenRA.GameRules if (target.Type == TargetType.Terrain) { var cell = target.CenterPosition.ToCPos(); - if (!world.Map.IsInMap(cell)) + if (!world.Map.Contains(cell)) return false; var cellInfo = world.Map.GetTerrainInfo(cell); diff --git a/OpenRA.Game/Graphics/Minimap.cs b/OpenRA.Game/Graphics/Minimap.cs index d99d02cdbc..c766273883 100644 --- a/OpenRA.Game/Graphics/Minimap.cs +++ b/OpenRA.Game/Graphics/Minimap.cs @@ -74,8 +74,9 @@ namespace OpenRA.Graphics continue; var res = resourceRules.Actors["world"].Traits.WithInterface() - .Where(t => t.ResourceType == map.MapResources.Value[mapX, mapY].Type) + .Where(t => t.ResourceType == map.MapResources.Value[mapX, mapY].Type) .Select(t => t.TerrainType).FirstOrDefault(); + if (res == null) continue; @@ -135,7 +136,7 @@ namespace OpenRA.Graphics var color = t.Trait.RadarSignatureColor(t.Actor); foreach (var cell in t.Trait.RadarSignatureCells(t.Actor)) - if (world.Map.IsInMap(cell)) + if (world.Map.Contains(cell)) *(c + ((cell.Y - world.Map.Bounds.Top) * bitmapData.Stride >> 2) + cell.X - world.Map.Bounds.Left) = color.ToArgb(); } } diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index fe6cb84410..7d55487720 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -28,15 +28,12 @@ namespace OpenRA.Graphics var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width]; var nv = 0; - for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++) + foreach (var cell in map.Cells) { - for (var i = map.Bounds.Left; i < map.Bounds.Right; i++) - { - var tile = wr.Theater.TileSprite(map.MapTiles.Value[i, j]); - var pos = wr.ScreenPosition(new CPos(i, j).CenterPosition) - 0.5f * tile.size; - Util.FastCreateQuad(vertices, pos, tile, terrainPalette, nv, tile.size); - nv += 4; - } + var tile = wr.Theater.TileSprite(map.MapTiles.Value[cell]); + var pos = wr.ScreenPosition(cell.CenterPosition) - 0.5f * tile.size; + Util.FastCreateQuad(vertices, pos, tile, terrainPalette, nv, tile.size); + nv += 4; } vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length); @@ -46,9 +43,9 @@ namespace OpenRA.Graphics public void Draw(WorldRenderer wr, Viewport viewport) { var verticesPerRow = 4*map.Bounds.Width; - var bounds = viewport.CellBounds; - var firstRow = bounds.Top - map.Bounds.Top; - var lastRow = bounds.Bottom - map.Bounds.Top; + var cells = viewport.VisibleCells; + var firstRow = cells.TopLeft.Y - map.Bounds.Top; + var lastRow = cells.BottomRight.Y - map.Bounds.Top + 1; if (lastRow < 0 || firstRow > map.Bounds.Height) return; diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs index c7b136b5ed..141e3ddd02 100644 --- a/OpenRA.Game/Graphics/Theater.cs +++ b/OpenRA.Game/Graphics/Theater.cs @@ -71,7 +71,7 @@ namespace OpenRA.Graphics missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1)); } - public Sprite TileSprite(TileReference r) + public Sprite TileSprite(TerrainTile r) { Sprite[] template; if (!templates.TryGetValue(r.Type, out template)) diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 4c61e2e18c..1dd6a5f881 100755 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -46,7 +47,8 @@ namespace OpenRA.Graphics public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } } public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } } int2 viewportSize; - bool cellBoundsDirty = true; + CellRegion cells; + bool cellsDirty = true; float zoom = 1f; public float Zoom @@ -60,7 +62,7 @@ namespace OpenRA.Graphics { zoom = value; viewportSize = (1f / zoom * new float2(Game.Renderer.Resolution)).ToInt2(); - cellBoundsDirty = true; + cellsDirty = true; } } @@ -110,14 +112,14 @@ namespace OpenRA.Graphics public void Center(WPos pos) { CenterLocation = worldRenderer.ScreenPxPosition(pos).Clamp(mapBounds); - cellBoundsDirty = true; + cellsDirty = true; } public void Scroll(float2 delta, bool ignoreBorders) { // Convert scroll delta from world-px to viewport-px CenterLocation += (1f / Zoom * delta).ToInt2(); - cellBoundsDirty = true; + cellsDirty = true; if (!ignoreBorders) CenterLocation = CenterLocation.Clamp(mapBounds); @@ -129,33 +131,30 @@ namespace OpenRA.Graphics { get { - var r = CellBounds; - var ctl = new CPos(r.Left, r.Top).TopLeft; - var cbr = new CPos(r.Right, r.Bottom).TopLeft; + var ctl = VisibleCells.TopLeft.TopLeft; + var cbr = VisibleCells.BottomRight.BottomRight; var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl)).Clamp(ScreenClip); var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr)).Clamp(ScreenClip); return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y); } } - // Rectangle (in cell coords) of cells that are currently visible on the screen - Rectangle cachedRect; - public Rectangle CellBounds + public CellRegion VisibleCells { get { - if (cellBoundsDirty) + if (cellsDirty) { - var boundary = new CVec(1, 1); - var tl = worldRenderer.Position(TopLeft).ToCPos() - boundary; - var br = worldRenderer.Position(BottomRight).ToCPos() + boundary; + // Calculate the intersection of the visible rectangle and the map. + var map = worldRenderer.world.Map; + var tl = map.Clamp(worldRenderer.Position(TopLeft).ToCPos() - new CVec(1, 1)); + var br = map.Clamp(worldRenderer.Position(BottomRight).ToCPos()); - cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), worldRenderer.world.Map.Bounds); - cellBoundsDirty = false; + cells = new CellRegion(tl, br); + cellsDirty = false; } - var b = worldRenderer.world.VisibleBounds; - return b.HasValue ? Rectangle.Intersect(cachedRect, b.Value) : cachedRect; + return cells; } } } diff --git a/OpenRA.Game/Map/CellLayer.cs b/OpenRA.Game/Map/CellLayer.cs new file mode 100644 index 0000000000..7f973b8902 --- /dev/null +++ b/OpenRA.Game/Map/CellLayer.cs @@ -0,0 +1,105 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Graphics; + +namespace OpenRA +{ + // Represents a layer of "something" that covers the map + public class CellLayer : IEnumerable + { + public readonly Size Size; + T[] entries; + + public CellLayer(Map map) + : this(new Size(map.MapSize.X, map.MapSize.Y)) { } + + public CellLayer(Size size) + { + Size = size; + entries = new T[size.Width * size.Height]; + } + + // Resolve an array index from cell coordinates + int Index(CPos cell) + { + // This will eventually define a distinct case for diagonal cell grids + return cell.Y * Size.Width + cell.X; + } + + /// Gets or sets the using cell coordinates + public T this[CPos cell] + { + get + { + return entries[Index(cell)]; + } + + set + { + entries[Index(cell)] = value; + } + } + + /// Gets or sets the layer contents using raw map coordinates (not CPos!) + public T this[int u, int v] + { + get + { + return entries[v * Size.Width + u]; + } + + set + { + entries[v * Size.Width + u] = value; + } + } + + /// Clears the layer contents with a known value + public void Clear(T clearValue) + { + for (var i = 0; i < entries.Length; i++) + entries[i] = clearValue; + } + + public IEnumerator GetEnumerator() + { + return (IEnumerator)entries.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + // Helper functions + public static class CellLayer + { + /// Create a new layer by resizing another layer. New cells are filled with defaultValue. + public static CellLayer Resize(CellLayer layer, Size newSize, T defaultValue) + { + var result = new CellLayer(newSize); + var width = Math.Min(layer.Size.Width, newSize.Width); + var height = Math.Min(layer.Size.Height, newSize.Height); + + result.Clear(defaultValue); + for (var j = 0; j < height; j++) + for (var i = 0; i < width; i++) + result[i, j] = layer[i, j]; + + return result; + } + } +} diff --git a/OpenRA.Game/Map/CellRegion.cs b/OpenRA.Game/Map/CellRegion.cs new file mode 100644 index 0000000000..3cdccc7fbe --- /dev/null +++ b/OpenRA.Game/Map/CellRegion.cs @@ -0,0 +1,101 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Graphics; + +namespace OpenRA +{ + // Represents a (on-screen) rectangular collection of tiles. + // TopLeft and BottomRight are inclusive + public class CellRegion : IEnumerable + { + // Corners of the region + public readonly CPos TopLeft; + public readonly CPos BottomRight; + + // Corners in map coordinates + // Defined for forward compatibility with diagonal cell grids + readonly CPos mapTopLeft; + readonly CPos mapBottomRight; + + public CellRegion(CPos topLeft, CPos bottomRight) + { + TopLeft = topLeft; + BottomRight = bottomRight; + + mapTopLeft = TopLeft; + mapBottomRight = BottomRight; + } + + public bool Contains(CPos cell) + { + // Defined for forward compatibility with diagonal cell grids + var uv = cell; + return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y; + } + + public IEnumerator GetEnumerator() + { + return new CellRegionEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + class CellRegionEnumerator : IEnumerator + { + readonly CellRegion r; + + // Current position, in map coordinates + int u, v; + + public CellRegionEnumerator(CellRegion region) + { + r = region; + Reset(); + } + + public bool MoveNext() + { + u += 1; + + // Check for column overflow + if (u > r.mapBottomRight.X) + { + v += 1; + u = r.mapTopLeft.X; + + // Check for row overflow + if (v > r.mapBottomRight.Y) + return false; + } + + return true; + } + + public void Reset() + { + // Enumerator starts *before* the first element in the sequence. + u = r.mapTopLeft.X - 1; + v = r.mapTopLeft.Y; + } + + public CPos Current { get { return new CPos(u, v); } } + object IEnumerator.Current { get { return Current; } } + public void Dispose() { } + } + } +} diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 01f5685963..06f3b9019d 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; using System.IO; @@ -110,29 +111,38 @@ namespace OpenRA [FieldLoader.Ignore] public byte TileFormat = 1; public int2 MapSize; - [FieldLoader.Ignore] public Lazy[,]> MapTiles; - [FieldLoader.Ignore] public Lazy[,]> MapResources; - [FieldLoader.Ignore] public int[,] CustomTerrain; + [FieldLoader.Ignore] public Lazy> MapTiles; + [FieldLoader.Ignore] public Lazy> MapResources; + [FieldLoader.Ignore] public CellLayer CustomTerrain; [FieldLoader.Ignore] Lazy rules; public Ruleset Rules { get { return rules != null ? rules.Value : null; } } public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } } + [FieldLoader.Ignore] public CellRegion Cells; + public static Map FromTileset(TileSet tileset) { - var tile = tileset.Templates.First(); - var tileRef = new TileReference { Type = tile.Key, Index = (byte)0 }; + var size = new Size(1, 1); + var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0); + + var makeMapTiles = Exts.Lazy(() => + { + var ret = new CellLayer(size); + ret.Clear(tileRef); + return ret; + }); var map = new Map() { Title = "Name your map here", Description = "Describe your map here", Author = "Your name here", - MapSize = new int2(1, 1), + MapSize = new int2(size), Tileset = tileset.Id, Options = new MapOptions(), - MapResources = Exts.Lazy(() => new TileReference[1, 1]), - MapTiles = Exts.Lazy(() => new TileReference[1, 1] { { tileRef } }), + MapResources = Exts.Lazy(() => new CellLayer(size)), + MapTiles = makeMapTiles, Actors = Exts.Lazy(() => new Dictionary()), Smudges = Exts.Lazy(() => new List()) }; @@ -229,11 +239,6 @@ namespace OpenRA NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications"); TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations"); - CustomTerrain = new int[MapSize.X, MapSize.Y]; - for (var x = 0; x < MapSize.X; x++) - for (var y = 0; y < MapSize.Y; y++) - CustomTerrain[x, y] = -1; - MapTiles = Exts.Lazy(() => LoadMapTiles()); MapResources = Exts.Lazy(() => LoadResourceTiles()); @@ -254,6 +259,14 @@ namespace OpenRA void PostInit() { rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this)); + + var tl = new CPos(Bounds.Left, Bounds.Top); + var br = new CPos(Bounds.Right - 1, Bounds.Bottom - 1); + Cells = new CellRegion(tl, br); + + CustomTerrain = new CellLayer(this); + foreach (var cell in Cells) + CustomTerrain[cell] = -1; } public Ruleset PreloadRules() @@ -346,9 +359,9 @@ namespace OpenRA Container.Write(entries); } - public TileReference[,] LoadMapTiles() + public CellLayer LoadMapTiles() { - var tiles = new TileReference[MapSize.X, MapSize.Y]; + var tiles = new CellLayer(this); using (var dataStream = Container.GetContent("map.bin")) { if (dataStream.ReadUInt8() != 1) @@ -365,23 +378,27 @@ namespace OpenRA var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3); var d = 0; for (var i = 0; i < MapSize.X; i++) + { for (var j = 0; j < MapSize.Y; j++) { var tile = BitConverter.ToUInt16(data, d); d += 2; + var index = data[d++]; if (index == byte.MaxValue) index = (byte)(i % 4 + (j % 4) * 4); - tiles[i, j] = new TileReference(tile, index); + + tiles[i, j] = new TerrainTile(tile, index); } + } } return tiles; } - public TileReference[,] LoadResourceTiles() + public CellLayer LoadResourceTiles() { - var resources = new TileReference[MapSize.X, MapSize.Y]; + var resources = new CellLayer(this); using (var dataStream = Container.GetContent("map.bin")) { @@ -400,10 +417,11 @@ namespace OpenRA 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 j = 0; j < MapSize.Y; j++) - resources[i, j] = new TileReference(data[d++], data[d++]); + resources[i, j] = new ResourceTile(data[d++], data[d++]); } return resources; @@ -423,38 +441,43 @@ namespace OpenRA for (var i = 0; i < MapSize.X; i++) for (var j = 0; j < MapSize.Y; j++) { - writer.Write(MapTiles.Value[i, j].Type); - writer.Write(MapTiles.Value[i, j].Index); + var tile = MapTiles.Value[new CPos(i, j)]; + writer.Write(tile.Type); + writer.Write(tile.Index); } // Resource data for (var i = 0; i < MapSize.X; i++) + { for (var j = 0; j < MapSize.Y; j++) { - writer.Write(MapResources.Value[i, j].Type); - writer.Write(MapResources.Value[i, j].Index); + var tile = MapResources.Value[new CPos(i, j)]; + writer.Write(tile.Type); + writer.Write(tile.Index); } + } } return dataStream.ToArray(); } - public bool IsInMap(CPos xy) { return IsInMap(xy.X, xy.Y); } - public bool IsInMap(int x, int y) { return Bounds.Contains(x, y); } + public bool Contains(CPos xy) { return Bounds.Contains(xy.X, xy.Y); } public void Resize(int width, int height) // editor magic. { var oldMapTiles = MapTiles.Value; var oldMapResources = MapResources.Value; + var newSize = new Size(width, height); - MapTiles = Exts.Lazy(() => Exts.ResizeArray(oldMapTiles, oldMapTiles[0, 0], width, height)); - MapResources = Exts.Lazy(() => Exts.ResizeArray(oldMapResources, oldMapResources[0, 0], width, height)); - MapSize = new int2(width, height); + MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0])); + MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0])); + MapSize = new int2(newSize); } public void ResizeCordon(int left, int top, int right, int bottom) { Bounds = Rectangle.FromLTRB(left, top, right, bottom); + Cells = new CellRegion(new CPos(Bounds.Left, Bounds.Top), new CPos(Bounds.Right - 1, Bounds.Bottom - 1)); } string ComputeHash() @@ -524,26 +547,30 @@ namespace OpenRA { for (var i = Bounds.Left; i < Bounds.Right; i++) { - var tr = MapTiles.Value[i, j]; - if (!tileset.Templates.ContainsKey(tr.Type)) + var cell = new CPos(i, j); + var type = MapTiles.Value[cell].Type; + var index = MapTiles.Value[cell].Index; + if (!tileset.Templates.ContainsKey(type)) { - Console.WriteLine("Unknown Tile ID {0}".F(tr.Type)); + Console.WriteLine("Unknown Tile ID {0}".F(type)); continue; } - var template = tileset.Templates[tr.Type]; + + var template = tileset.Templates[type]; if (!template.PickAny) continue; - tr.Index = (byte)r.Next(0, template.TilesCount); - MapTiles.Value[i, j] = tr; + + index = (byte)r.Next(0, template.TilesCount); + MapTiles.Value[cell] = new TerrainTile(type, index); } } } public int GetTerrainIndex(CPos cell) { - var custom = CustomTerrain[cell.X, cell.Y]; + var custom = CustomTerrain[cell]; var tileSet = Rules.TileSets[Tileset]; - return custom != -1 ? custom : tileSet.GetTerrainIndex(MapTiles.Value[cell.X, cell.Y]); + return custom != -1 ? custom : tileSet.GetTerrainIndex(MapTiles.Value[cell]); } public TerrainTypeInfo GetTerrainInfo(CPos cell) @@ -606,12 +633,12 @@ namespace OpenRA if (range >= TilesByDistance.Length) throw new InvalidOperationException("FindTilesInCircle supports queries for only <= {0}".F(MaxTilesInCircleRange)); - for(var i = 0; i <= range; i++) + for (var i = 0; i <= range; i++) { - foreach(var offset in TilesByDistance[i]) + foreach (var offset in TilesByDistance[i]) { var t = offset + center; - if (Bounds.Contains(t.X, t.Y)) + if (Contains(t)) yield return t; } } diff --git a/OpenRA.Game/Map/TileReference.cs b/OpenRA.Game/Map/TileReference.cs index 5dc7d719b8..1d835888a5 100644 --- a/OpenRA.Game/Map/TileReference.cs +++ b/OpenRA.Game/Map/TileReference.cs @@ -10,15 +10,29 @@ namespace OpenRA { - public struct TileReference + public struct TerrainTile { - public T Type; - public U Index; + public readonly ushort Type; + public readonly byte Index; - public TileReference(T t, U i) + public TerrainTile(ushort type, byte index) { - Type = t; - Index = i; + Type = type; + Index = index; + } + + public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); } + } + + public struct ResourceTile + { + public readonly byte Type; + public readonly byte Index; + + public ResourceTile(byte type, byte index) + { + Type = type; + Index = index; } public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); } diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index 1397cd5eea..1afa545d16 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -223,7 +223,7 @@ namespace OpenRA throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type)); } - public int GetTerrainIndex(TileReference r) + public int GetTerrainIndex(TerrainTile r) { var tpl = Templates[r.Type]; @@ -261,7 +261,7 @@ namespace OpenRA root.WriteToFile(filepath); } - public TerrainTypeInfo GetTerrainInfo(TileReference r) + public TerrainTypeInfo GetTerrainInfo(TerrainTile r) { return terrainInfo[GetTerrainIndex(r)]; } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 0657df79df..d41cd7a7c4 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -235,6 +235,8 @@ + + @@ -358,4 +360,4 @@ --> - \ No newline at end of file + diff --git a/OpenRA.Game/Orders/GenericSelectTarget.cs b/OpenRA.Game/Orders/GenericSelectTarget.cs index c1499d18d2..a6048754ca 100644 --- a/OpenRA.Game/Orders/GenericSelectTarget.cs +++ b/OpenRA.Game/Orders/GenericSelectTarget.cs @@ -55,7 +55,7 @@ namespace OpenRA.Orders IEnumerable OrderInner(World world, CPos xy, MouseInput mi) { - if (mi.Button == expectedButton && world.Map.IsInMap(xy)) + if (mi.Button == expectedButton && world.Map.Contains(xy)) { world.CancelInputMode(); foreach (var subject in subjects) @@ -66,6 +66,6 @@ namespace OpenRA.Orders public virtual void Tick(World world) { } public IEnumerable Render(WorldRenderer wr, World world) { yield break; } public void RenderAfterWorld(WorldRenderer wr, World world) { } - public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; } + public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; } } } diff --git a/OpenRA.Game/Traits/World/ActorMap.cs b/OpenRA.Game/Traits/World/ActorMap.cs index c85de0c256..fd28f527c2 100644 --- a/OpenRA.Game/Traits/World/ActorMap.cs +++ b/OpenRA.Game/Traits/World/ActorMap.cs @@ -41,7 +41,7 @@ namespace OpenRA.Traits readonly ActorMapInfo info; readonly Map map; - InfluenceNode[,] influence; + readonly CellLayer influence; List[] actors; int rows, cols; @@ -56,7 +56,7 @@ namespace OpenRA.Traits { this.info = info; map = world.Map; - influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y]; + influence = new CellLayer(world.Map); cols = world.Map.MapSize.X / info.BinSize + 1; rows = world.Map.MapSize.Y / info.BinSize + 1; @@ -71,20 +71,20 @@ namespace OpenRA.Traits public IEnumerable GetUnitsAt(CPos a) { - if (!map.IsInMap(a)) + if (!map.Contains(a)) yield break; - for (var i = influence[a.X, a.Y]; i != null; i = i.Next) + for (var i = influence[a]; i != null; i = i.Next) if (!i.Actor.Destroyed) yield return i.Actor; } public IEnumerable GetUnitsAt(CPos a, SubCell sub) { - if (!map.IsInMap(a)) + if (!map.Contains(a)) yield break; - for (var i = influence[a.X, a.Y]; i != null; i = i.Next) + for (var i = influence[a]; i != null; i = i.Next) if (!i.Actor.Destroyed && (i.SubCell == sub || i.SubCell == SubCell.FullCell)) yield return i.Actor; } @@ -107,12 +107,12 @@ namespace OpenRA.Traits public bool AnyUnitsAt(CPos a) { - return influence[a.X, a.Y] != null; + return influence[a] != null; } public bool AnyUnitsAt(CPos a, SubCell sub) { - for (var i = influence[a.X, a.Y]; i != null; i = i.Next) + for (var i = influence[a]; i != null; i = i.Next) if (i.SubCell == sub || i.SubCell == SubCell.FullCell) return true; @@ -122,20 +122,25 @@ namespace OpenRA.Traits public void AddInfluence(Actor self, IOccupySpace ios) { foreach (var c in ios.OccupiedCells()) - influence[c.First.X, c.First.Y] = new InfluenceNode { Next = influence[c.First.X, c.First.Y], SubCell = c.Second, Actor = self }; + influence[c.First] = new InfluenceNode { Next = influence[c.First], SubCell = c.Second, Actor = self }; } public void RemoveInfluence(Actor self, IOccupySpace ios) { foreach (var c in ios.OccupiedCells()) - RemoveInfluenceInner(ref influence[c.First.X, c.First.Y], self); + { + var temp = influence[c.First]; + RemoveInfluenceInner(ref temp, self); + influence[c.First] = temp; + } } void RemoveInfluenceInner(ref InfluenceNode influenceNode, Actor toRemove) { if (influenceNode == null) return; - else if (influenceNode.Actor == toRemove) + + if (influenceNode.Actor == toRemove) influenceNode = influenceNode.Next; if (influenceNode != null) @@ -206,5 +211,11 @@ namespace OpenRA.Traits } } } + + public IEnumerable ActorsInWorld() + { + return actors.SelectMany(a => a.Where(b => b.IsInWorld)) + .Distinct(); + } } } diff --git a/OpenRA.Game/Traits/World/ResourceLayer.cs b/OpenRA.Game/Traits/World/ResourceLayer.cs index 1efc612ddc..0f405e1931 100644 --- a/OpenRA.Game/Traits/World/ResourceLayer.cs +++ b/OpenRA.Game/Traits/World/ResourceLayer.cs @@ -22,89 +22,78 @@ namespace OpenRA.Traits static readonly CellContents EmptyCell = new CellContents(); World world; - protected CellContents[,] content; - protected CellContents[,] render; + protected CellLayer content; + protected CellLayer render; List dirty; public void Render(WorldRenderer wr) { - var clip = wr.Viewport.CellBounds; - for (var x = clip.Left; x < clip.Right; x++) + foreach (var cell in wr.Viewport.VisibleCells) { - for (var y = clip.Top; y < clip.Bottom; y++) - { - var pos = new CPos(x, y); - if (world.ShroudObscures(pos)) - continue; + if (world.ShroudObscures(cell)) + continue; - var c = render[x, y]; - if (c.Sprite != null) - new SpriteRenderable(c.Sprite, pos.CenterPosition, - WVec.Zero, -511, c.Type.Palette, 1f, true).Render(wr); - } + var c = render[cell]; + if (c.Sprite != null) + new SpriteRenderable(c.Sprite, cell.CenterPosition, + WVec.Zero, -511, c.Type.Palette, 1f, true).Render(wr); } } - int GetAdjacentCellsWith(ResourceType t, int i, int j) + int GetAdjacentCellsWith(ResourceType t, CPos cell) { var sum = 0; for (var u = -1; u < 2; u++) for (var v = -1; v < 2; v++) - if (content[i + u, j + v].Type == t) + if (content[cell + new CVec(u, v)].Type == t) ++sum; + return sum; } public void WorldLoaded(World w, WorldRenderer wr) { this.world = w; - content = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y]; - render = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y]; + content = new CellLayer(w.Map); + render = new CellLayer(w.Map); dirty = new List(); var resources = w.WorldActor.TraitsImplementing() .ToDictionary(r => r.Info.ResourceType, r => r); - var map = w.Map; - for (var x = map.Bounds.Left; x < map.Bounds.Right; x++) + foreach (var cell in w.Map.Cells) { - for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++) - { - var cell = new CPos(x, y); - ResourceType t; - if (!resources.TryGetValue(w.Map.MapResources.Value[x, y].Type, out t)) - continue; + ResourceType t; + if (!resources.TryGetValue(w.Map.MapResources.Value[cell].Type, out t)) + continue; - if (!AllowResourceAt(t, cell)) - continue; + if (!AllowResourceAt(t, cell)) + continue; - content[x, y] = CreateResourceCell(t, cell); - } + content[cell] = CreateResourceCell(t, cell); } // Set initial density based on the number of neighboring resources - for (var x = map.Bounds.Left; x < map.Bounds.Right; x++) + foreach (var cell in w.Map.Cells) { - for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++) + var type = content[cell].Type; + if (type != null) { - var type = content[x, y].Type; - if (type != null) - { - // Adjacent includes the current cell, so is always >= 1 - var adjacent = GetAdjacentCellsWith(type, x, y); - var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9); - content[x, y].Density = Math.Max(density, 1); + // Adjacent includes the current cell, so is always >= 1 + var adjacent = GetAdjacentCellsWith(type, cell); + var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9); + var temp = content[cell]; + temp.Density = Math.Max(density, 1); - render[x, y] = content[x, y]; - UpdateRenderedSprite(new CPos(x, y)); - } + render[cell] = content[cell] = temp; + UpdateRenderedSprite(cell); } } } - protected virtual void UpdateRenderedSprite(CPos p) + protected virtual void UpdateRenderedSprite(CPos cell) { - var t = render[p.X, p.Y]; + var t = render[cell]; if (t.Density > 0) { var sprites = t.Type.Variants[t.Variant]; @@ -114,7 +103,7 @@ namespace OpenRA.Traits else t.Sprite = null; - render[p.X, p.Y] = t; + render[cell] = t; } protected virtual string ChooseRandomVariant(ResourceType t) @@ -129,7 +118,7 @@ namespace OpenRA.Traits { if (!self.World.FogObscures(c)) { - render[c.X, c.Y] = content[c.X, c.Y]; + render[c] = content[c]; UpdateRenderedSprite(c); remove.Add(c); } @@ -139,15 +128,15 @@ namespace OpenRA.Traits dirty.Remove(r); } - public bool AllowResourceAt(ResourceType rt, CPos a) + public bool AllowResourceAt(ResourceType rt, CPos cell) { - if (!world.Map.IsInMap(a.X, a.Y)) + if (!world.Map.Contains(cell)) return false; - if (!rt.Info.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type)) + if (!rt.Info.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type)) return false; - if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(a)) + if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(cell)) return false; return true; @@ -160,9 +149,10 @@ namespace OpenRA.Traits || (currentResourceType == null && AllowResourceAt(newResourceType, cell)); } - CellContents CreateResourceCell(ResourceType t, CPos p) + CellContents CreateResourceCell(ResourceType t, CPos cell) { - world.Map.CustomTerrain[p.X, p.Y] = world.TileSet.GetTerrainIndex(t.Info.TerrainType); + world.Map.CustomTerrain[cell] = world.TileSet.GetTerrainIndex(t.Info.TerrainType); + return new CellContents { Type = t, @@ -172,7 +162,7 @@ namespace OpenRA.Traits public void AddResource(ResourceType t, CPos p, int n) { - var cell = content[p.X, p.Y]; + var cell = content[p]; if (cell.Type == null) cell = CreateResourceCell(t, p); @@ -180,58 +170,60 @@ namespace OpenRA.Traits return; cell.Density = Math.Min(cell.Type.Info.MaxDensity, cell.Density + n); - content[p.X, p.Y] = cell; + content[p] = cell; if (!dirty.Contains(p)) dirty.Add(p); } - public bool IsFull(CPos c) + public bool IsFull(CPos cell) { - return content[c.X, c.Y].Density == content[c.X, c.Y].Type.Info.MaxDensity; + return content[cell].Density == content[cell].Type.Info.MaxDensity; } - public ResourceType Harvest(CPos p) + public ResourceType Harvest(CPos cell) { - var type = content[p.X, p.Y].Type; - if (type == null) + var c = content[cell]; + if (c.Type == null) return null; - if (--content[p.X, p.Y].Density < 0) + if (--c.Density < 0) { - content[p.X, p.Y] = EmptyCell; - world.Map.CustomTerrain[p.X, p.Y] = -1; + content[cell] = EmptyCell; + world.Map.CustomTerrain[cell] = -1; } + else + content[cell] = c; - if (!dirty.Contains(p)) - dirty.Add(p); + if (!dirty.Contains(cell)) + dirty.Add(cell); - return type; + return c.Type; } - public void Destroy(CPos p) + public void Destroy(CPos cell) { // Don't break other users of CustomTerrain if there are no resources - if (content[p.X, p.Y].Type == null) + if (content[cell].Type == null) return; // Clear cell - content[p.X, p.Y] = EmptyCell; - world.Map.CustomTerrain[p.X, p.Y] = -1; + content[cell] = EmptyCell; + world.Map.CustomTerrain[cell] = -1; - if (!dirty.Contains(p)) - dirty.Add(p); + if (!dirty.Contains(cell)) + dirty.Add(cell); } - public ResourceType GetResource(CPos p) { return content[p.X, p.Y].Type; } - public ResourceType GetRenderedResource(CPos p) { return render[p.X, p.Y].Type; } - public int GetResourceDensity(CPos p) { return content[p.X, p.Y].Density; } - public int GetMaxResourceDensity(CPos p) + public ResourceType GetResource(CPos cell) { return content[cell].Type; } + public ResourceType GetRenderedResource(CPos cell) { return render[cell].Type; } + public int GetResourceDensity(CPos cell) { return content[cell].Density; } + public int GetMaxResourceDensity(CPos cell) { - if (content[p.X, p.Y].Type == null) + if (content[cell].Type == null) return 0; - return content[p.X, p.Y].Type.Info.MaxDensity; + return content[cell].Type.Info.MaxDensity; } public struct CellContents diff --git a/OpenRA.Game/Traits/World/Shroud.cs b/OpenRA.Game/Traits/World/Shroud.cs index c4804e535e..154dc66384 100644 --- a/OpenRA.Game/Traits/World/Shroud.cs +++ b/OpenRA.Game/Traits/World/Shroud.cs @@ -27,9 +27,9 @@ namespace OpenRA.Traits Actor self; Map map; - int[,] visibleCount; - int[,] generatedShroudCount; - bool[,] explored; + CellLayer visibleCount; + CellLayer generatedShroudCount; + CellLayer explored; readonly Lazy fogVisibilities; @@ -38,8 +38,6 @@ namespace OpenRA.Traits Dictionary visibility = new Dictionary(); Dictionary generation = new Dictionary(); - public Rectangle ExploredBounds { get; private set; } - public int Hash { get; private set; } public Shroud(Actor self) @@ -47,9 +45,9 @@ namespace OpenRA.Traits this.self = self; map = self.World.Map; - visibleCount = new int[map.MapSize.X, map.MapSize.Y]; - generatedShroudCount = new int[map.MapSize.X, map.MapSize.Y]; - explored = new bool[map.MapSize.X, map.MapSize.Y]; + visibleCount = new CellLayer(map); + generatedShroudCount = new CellLayer(map); + explored = new CellLayer(map); self.World.ActorAdded += AddVisibility; self.World.ActorRemoved += RemoveVisibility; @@ -57,9 +55,6 @@ namespace OpenRA.Traits self.World.ActorAdded += AddShroudGeneration; self.World.ActorRemoved += RemoveShroudGeneration; - if (!self.World.LobbyInfo.GlobalSettings.Shroud) - ExploredBounds = map.Bounds; - fogVisibilities = Exts.Lazy(() => self.TraitsImplementing().ToArray()); } @@ -71,15 +66,12 @@ namespace OpenRA.Traits static IEnumerable FindVisibleTiles(World world, CPos position, WRange radius) { var r = (radius.Range + 1023) / 1024; - var min = (position - new CVec(r, r)).Clamp(world.Map.Bounds); - var max = (position + new CVec(r, r)).Clamp(world.Map.Bounds); - - var circleArea = radius.Range * radius.Range; + var limit = radius.Range * radius.Range; var pos = position.CenterPosition; - for (var j = min.Y; j <= max.Y; j++) - for (var i = min.X; i <= max.X; i++) - if (circleArea >= (new CPos(i, j).CenterPosition - pos).LengthSquared) - yield return new CPos(i, j); + + foreach (var cell in world.Map.FindTilesInCircle(position, r)) + if ((cell.CenterPosition - pos).HorizontalLengthSquared <= limit) + yield return cell; } void AddVisibility(Actor a) @@ -92,20 +84,11 @@ namespace OpenRA.Traits var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range)) .Distinct().ToArray(); - // Update bounding rect - var r = (rs.Range.Range + 1023) / 1024; - - foreach (var o in origins) - { - var box = new Rectangle(o.X - r, o.Y - r, 2 * r + 1, 2 * r + 1); - ExploredBounds = Rectangle.Union(ExploredBounds, box); - } - // Update visibility foreach (var c in visible) { - visibleCount[c.X, c.Y]++; - explored[c.X, c.Y] = true; + visibleCount[c]++; + explored[c] = true; } if (visibility.ContainsKey(a)) @@ -122,7 +105,7 @@ namespace OpenRA.Traits return; foreach (var c in visible) - visibleCount[c.X, c.Y]--; + visibleCount[c]--; visibility.Remove(a); Invalidate(); @@ -147,7 +130,7 @@ namespace OpenRA.Traits var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range)) .Distinct().ToArray(); foreach (var c in shrouded) - generatedShroudCount[c.X, c.Y]++; + generatedShroudCount[c]++; if (generation.ContainsKey(a)) throw new InvalidOperationException("Attempting to add duplicate shroud generation"); @@ -163,7 +146,7 @@ namespace OpenRA.Traits return; foreach (var c in shrouded) - generatedShroudCount[c.X, c.Y]--; + generatedShroudCount[c]--; generation.Remove(a); Invalidate(); @@ -203,55 +186,44 @@ namespace OpenRA.Traits public void Explore(World world, CPos center, WRange range) { foreach (var q in FindVisibleTiles(world, center, range)) - explored[q.X, q.Y] = true; - - var r = (range.Range + 1023) / 1024; - var box = new Rectangle(center.X - r, center.Y - r, 2 * r + 1, 2 * r + 1); - ExploredBounds = Rectangle.Union(ExploredBounds, box); + explored[q] = true; Invalidate(); } public void Explore(Shroud s) { - for (var i = map.Bounds.Left; i < map.Bounds.Right; i++) - for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++) - if (s.explored[i,j] == true) - explored[i, j] = true; + foreach (var cell in map.Cells) + if (s.explored[cell]) + explored[cell] = true; - ExploredBounds = Rectangle.Union(ExploredBounds, s.ExploredBounds); + Invalidate(); } public void ExploreAll(World world) { - for (var i = map.Bounds.Left; i < map.Bounds.Right; i++) - for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++) - explored[i, j] = true; - - ExploredBounds = world.Map.Bounds; + explored.Clear(true); Invalidate(); } public void ResetExploration() { - for (var i = map.Bounds.Left; i < map.Bounds.Right; i++) - for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++) - explored[i, j] = visibleCount[i, j] > 0; + foreach (var cell in map.Cells) + explored[cell] = visibleCount[cell] > 0; Invalidate(); } - public bool IsExplored(CPos xy) { return IsExplored(xy.X, xy.Y); } - public bool IsExplored(int x, int y) + public bool IsExplored(CPos cell) { - if (!map.IsInMap(x, y)) + if (!map.Contains(cell)) return false; if (Disabled || !self.World.LobbyInfo.GlobalSettings.Shroud) return true; - return explored[x, y] && (generatedShroudCount[x, y] == 0 || visibleCount[x, y] > 0); + return explored[cell] && (generatedShroudCount[cell] == 0 || visibleCount[cell] > 0); } public bool IsExplored(Actor a) @@ -259,16 +231,15 @@ namespace OpenRA.Traits return GetVisOrigins(a).Any(o => IsExplored(o)); } - public bool IsVisible(CPos xy) { return IsVisible(xy.X, xy.Y); } - public bool IsVisible(int x, int y) + public bool IsVisible(CPos cell) { - if (!map.IsInMap(x, y)) + if (!map.Contains(cell)) return false; if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog) return true; - return visibleCount[x, y] > 0; + return visibleCount[cell] > 0; } // Actors are hidden under shroud, but not under fog by default diff --git a/OpenRA.Game/Widgets/ViewportControllerWidget.cs b/OpenRA.Game/Widgets/ViewportControllerWidget.cs index bdd1d213fd..bd5a5327ab 100644 --- a/OpenRA.Game/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Game/Widgets/ViewportControllerWidget.cs @@ -93,7 +93,7 @@ namespace OpenRA.Widgets { TooltipType = WorldTooltipType.None; var cell = worldRenderer.Position(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos)).ToCPos(); - if (!world.Map.IsInMap(cell)) + if (!world.Map.Contains(cell)) return; if (world.ShroudObscures(cell)) diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index 3b1e4e50be..4f9ab0454f 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -209,18 +209,20 @@ namespace OpenRA.Widgets } else if (key == Game.Settings.Keys.SelectUnitsByTypeKey) { - var selectedTypes = World.Selection.Actors.Where( - x => x.Owner == World.RenderPlayer).Select(a => a.Info); + var selectedTypes = World.Selection.Actors + .Where(x => x.Owner == World.RenderPlayer) + .Select(a => a.Info); + Func cond = a => a.Owner == World.RenderPlayer && selectedTypes.Contains(a.Info); - var newSelection = SelectActorsInBox( - World, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight, cond); + var tl = worldRenderer.Viewport.TopLeft; + var br = worldRenderer.Viewport.BottomRight; + var newSelection = SelectActorsInBox(World, tl, br, cond); + if (newSelection.Count() > selectedTypes.Count()) Game.Debug("Selected across screen"); else { - newSelection = World.ActorMap.ActorsInBox( - World.Map.Bounds.TopLeftAsCPos().TopLeft, - World.Map.Bounds.BottomRightAsCPos().BottomRight).Where(cond); + newSelection = World.ActorMap.ActorsInWorld().Where(cond); Game.Debug("Selected across map"); } diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 6e8d2c424d..6d1aa45475 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -55,17 +55,6 @@ namespace OpenRA public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); } public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); } - public Rectangle? VisibleBounds - { - get - { - if (RenderPlayer == null) - return null; - - return RenderPlayer.Shroud.ExploredBounds; - } - } - public bool IsReplay { get { return orderManager.Connection is ReplayConnection; } diff --git a/OpenRA.Mods.D2k/D2kResourceLayer.cs b/OpenRA.Mods.D2k/D2kResourceLayer.cs index 26ea2e87c2..7438e1948a 100644 --- a/OpenRA.Mods.D2k/D2kResourceLayer.cs +++ b/OpenRA.Mods.D2k/D2kResourceLayer.cs @@ -97,28 +97,28 @@ namespace OpenRA.Traits ClearSides FindClearSides(ResourceType t, CPos p) { var ret = ClearSides.None; - if (render[p.X, p.Y - 1].Type != t) + if (render[p + new CVec(0, -1)].Type != t) ret |= ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight; - if (render[p.X - 1, p.Y].Type != t) + if (render[p + new CVec(-1, 0)].Type != t) ret |= ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft; - if (render[p.X + 1, p.Y].Type != t) + if (render[p + new CVec(1, 0)].Type != t) ret |= ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight; - if (render[p.X, p.Y + 1].Type != t) + if (render[p + new CVec(0, 1)].Type != t) ret |= ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight; - if (render[p.X - 1, p.Y - 1].Type != t) + if (render[p + new CVec(-1, -1)].Type != t) ret |= ClearSides.TopLeft; - if (render[p.X + 1, p.Y - 1].Type != t) + if (render[p + new CVec(1, -1)].Type != t) ret |= ClearSides.TopRight; - if (render[p.X - 1, p.Y + 1].Type != t) + if (render[p + new CVec(-1, 1)].Type != t) ret |= ClearSides.BottomLeft; - if (render[p.X + 1, p.Y + 1].Type != t) + if (render[p + new CVec(1, 1)].Type != t) ret |= ClearSides.BottomRight; return ret; @@ -126,7 +126,7 @@ namespace OpenRA.Traits void UpdateRenderedTileInner(CPos p) { - var t = render[p.X, p.Y]; + var t = render[p]; if (t.Density > 0) { var clear = FindClearSides(t.Type, p); @@ -146,7 +146,7 @@ namespace OpenRA.Traits else t.Sprite = null; - render[p.X, p.Y] = t; + render[p] = t; } protected override void UpdateRenderedSprite(CPos p) diff --git a/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs b/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs index a2569415dc..d077a50de3 100755 --- a/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs +++ b/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs @@ -119,7 +119,7 @@ namespace OpenRA.Mods.RA.Activities var path = pathFinder.FindBidiPath( PathSearch.FromPoints(self.World, mobile.Info, self, searchCells, loc, true), - PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).InReverse() + PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).Reverse() ); inner = mobile.MoveTo(() => path); diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index 5400e021c9..cd05962639 100755 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -165,7 +165,7 @@ namespace OpenRA.Mods.RA.Air public bool CanLand(CPos cell) { - if (!self.World.Map.IsInMap(cell)) + if (!self.World.Map.Contains(cell)) return false; if (self.World.ActorMap.AnyUnitsAt(cell)) @@ -246,7 +246,7 @@ namespace OpenRA.Mods.RA.Air return false; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - cursor = self.World.Map.IsInMap(target.CenterPosition.ToCPos()) ? "move" : "move-blocked"; + cursor = self.World.Map.Contains(target.CenterPosition.ToCPos()) ? "move" : "move-blocked"; return true; } diff --git a/OpenRA.Mods.RA/Air/FlyTimed.cs b/OpenRA.Mods.RA/Air/FlyTimed.cs index c99fdc47a6..a904281e49 100755 --- a/OpenRA.Mods.RA/Air/FlyTimed.cs +++ b/OpenRA.Mods.RA/Air/FlyTimed.cs @@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA.Air { public override Activity Tick(Actor self) { - if (IsCanceled || !self.World.Map.IsInMap(self.Location)) + if (IsCanceled || !self.World.Map.Contains(self.Location)) return NextActivity; var plane = self.Trait(); diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index d5ab7465b1..77fd5e0ff8 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -207,7 +207,7 @@ namespace OpenRA.Mods.RA bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) { - if (!self.World.Map.IsInMap(location)) + if (!self.World.Map.Contains(location)) return false; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); diff --git a/OpenRA.Mods.RA/Bridge.cs b/OpenRA.Mods.RA/Bridge.cs index aa34689d44..6fa095944c 100644 --- a/OpenRA.Mods.RA/Bridge.cs +++ b/OpenRA.Mods.RA/Bridge.cs @@ -91,14 +91,14 @@ namespace OpenRA.Mods.RA // Set the initial custom terrain types foreach (var c in footprint.Keys) - self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c); + self.World.Map.CustomTerrain[c] = GetTerrainType(c); } int GetTerrainType(CPos cell) { var dx = cell - self.Location; var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y; - return self.World.TileSet.GetTerrainIndex(new TileReference(template, (byte)index)); + return self.World.TileSet.GetTerrainIndex(new TerrainTile(template, (byte)index)); } public void LinkNeighbouringBridges(World world, BridgeLayer bridges) @@ -121,7 +121,7 @@ namespace OpenRA.Mods.RA IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template) { return footprint.Select(c => (IRenderable)(new SpriteRenderable( - wr.Theater.TileSprite(new TileReference(template, c.Value)), + wr.Theater.TileSprite(new TerrainTile(template, c.Value)), c.Key.CenterPosition, WVec.Zero, -512, palette, 1f, true))).ToArray(); } @@ -204,7 +204,7 @@ namespace OpenRA.Mods.RA // Update map foreach (var c in footprint.Keys) - self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c); + self.World.Map.CustomTerrain[c] = GetTerrainType(c); // If this bridge repair operation connects two pathfinding domains, // update the domain index. diff --git a/OpenRA.Mods.RA/BridgeLayer.cs b/OpenRA.Mods.RA/BridgeLayer.cs index 365b47459c..62e3f1078e 100644 --- a/OpenRA.Mods.RA/BridgeLayer.cs +++ b/OpenRA.Mods.RA/BridgeLayer.cs @@ -19,95 +19,100 @@ namespace OpenRA.Mods.RA class BridgeLayerInfo : ITraitInfo { [ActorReference] - public readonly string[] Bridges = {"bridge1", "bridge2"}; + public readonly string[] Bridges = { "bridge1", "bridge2" }; public object Create(ActorInitializer init) { return new BridgeLayer(init.self, this); } } class BridgeLayer : IWorldLoaded { - readonly BridgeLayerInfo Info; + readonly BridgeLayerInfo info; readonly World world; - Dictionary> BridgeTypes = new Dictionary>(); - Bridge[,] Bridges; + Dictionary> bridgeTypes = new Dictionary>(); + CellLayer bridges; - public BridgeLayer(Actor self, BridgeLayerInfo Info) + public BridgeLayer(Actor self, BridgeLayerInfo info) { - this.Info = Info; + this.info = info; this.world = self.World; } public void WorldLoaded(World w, WorldRenderer wr) { - Bridges = new Bridge[w.Map.MapSize.X, w.Map.MapSize.Y]; + bridges = new CellLayer(w.Map); // Build a list of templates that should be overlayed with bridges - foreach(var bridge in Info.Bridges) + foreach (var bridge in info.Bridges) { var bi = w.Map.Rules.Actors[bridge].Traits.Get(); foreach (var template in bi.Templates) - BridgeTypes.Add(template.First, Pair.New(bridge, template.Second)); + bridgeTypes.Add(template.First, Pair.New(bridge, template.Second)); } // Loop through the map looking for templates to overlay for (var i = w.Map.Bounds.Left; i < w.Map.Bounds.Right; i++) + { for (var j = w.Map.Bounds.Top; j < w.Map.Bounds.Bottom; j++) - if (BridgeTypes.Keys.Contains(w.Map.MapTiles.Value[i, j].Type)) - ConvertBridgeToActor(w, i, j); + { + var cell = new CPos(i, j); + if (bridgeTypes.ContainsKey(w.Map.MapTiles.Value[cell].Type)) + ConvertBridgeToActor(w, cell); + } + } // Link adjacent (long)-bridges so that artwork is updated correctly foreach (var b in w.Actors.SelectMany(a => a.TraitsImplementing())) - b.LinkNeighbouringBridges(w,this); + b.LinkNeighbouringBridges(w, this); } - void ConvertBridgeToActor(World w, int i, int j) + void ConvertBridgeToActor(World w, CPos cell) { // This cell already has a bridge overlaying it from a previous iteration - if (Bridges[i,j] != null) + if (bridges[cell] != null) return; // Correlate the tile "image" aka subtile with its position to find the template origin - var tile = w.Map.MapTiles.Value[i, j].Type; - var index = w.Map.MapTiles.Value[i, j].Index; + var tile = w.Map.MapTiles.Value[cell].Type; + var index = w.Map.MapTiles.Value[cell].Index; var template = w.TileSet.Templates[tile]; - var ni = i - index % template.Size.X; - var nj = j - index / template.Size.X; + var ni = cell.X - index % template.Size.X; + var nj = cell.Y - index / template.Size.X; // Create a new actor for this bridge and keep track of which subtiles this bridge includes - var bridge = w.CreateActor(BridgeTypes[tile].First, new TypeDictionary + var bridge = w.CreateActor(bridgeTypes[tile].First, new TypeDictionary { new LocationInit(new CPos(ni, nj)), new OwnerInit(w.WorldActor.Owner), - new HealthInit(BridgeTypes[tile].Second), + new HealthInit(bridgeTypes[tile].Second), }).Trait(); var subTiles = new Dictionary(); // For each subtile in the template - for (byte ind = 0; ind < template.Size.X*template.Size.Y; ind++) + for (byte ind = 0; ind < template.Size.X * template.Size.Y; ind++) { // Where do we expect to find the subtile - var x = ni + ind % template.Size.X; - var y = nj + ind / template.Size.X; + var subtile = new CPos(ni + ind % template.Size.X, nj + ind / template.Size.X); // This isn't the bridge you're looking for - if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].Type != tile || - w.Map.MapTiles.Value[x, y].Index != ind) + if (!w.Map.Contains(subtile) || w.Map.MapTiles.Value[subtile].Type != tile || + w.Map.MapTiles.Value[subtile].Index != ind) continue; - subTiles.Add(new CPos(x, y), ind); - Bridges[x,y] = bridge; + subTiles.Add(subtile, ind); + bridges[subtile] = bridge; } + bridge.Create(tile, subTiles); } // Used to check for neighbouring bridges public Bridge GetBridge(CPos cell) { - if (!world.Map.IsInMap(cell)) + if (!world.Map.Contains(cell)) return null; - return Bridges[ cell.X, cell.Y ]; + return bridges[cell]; } } } diff --git a/OpenRA.Mods.RA/Buildings/BuildingInfluence.cs b/OpenRA.Mods.RA/Buildings/BuildingInfluence.cs index 6283a18ede..8ccaf15509 100755 --- a/OpenRA.Mods.RA/Buildings/BuildingInfluence.cs +++ b/OpenRA.Mods.RA/Buildings/BuildingInfluence.cs @@ -19,14 +19,14 @@ namespace OpenRA.Mods.RA.Buildings public class BuildingInfluence { - Actor[,] influence; + CellLayer influence; Map map; public BuildingInfluence(World world) { map = world.Map; - influence = new Actor[map.MapSize.X, map.MapSize.Y]; + influence = new CellLayer(map); world.ActorAdded += a => { @@ -35,8 +35,8 @@ namespace OpenRA.Mods.RA.Buildings return; foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b.Info, a.Location)) - if (map.IsInMap(u) && influence[u.X, u.Y] == null) - influence[u.X, u.Y] = a; + if (map.Contains(u) && influence[u] == null) + influence[u] = a; }; world.ActorRemoved += a => @@ -46,17 +46,17 @@ namespace OpenRA.Mods.RA.Buildings return; foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b.Info, a.Location)) - if (map.IsInMap(u) && influence[u.X, u.Y] == a) - influence[u.X, u.Y] = null; + if (map.Contains(u) && influence[u] == a) + influence[u] = null; }; } public Actor GetBuildingAt(CPos cell) { - if (!map.IsInMap(cell)) + if (!map.Contains(cell)) return null; - return influence[cell.X, cell.Y]; + return influence[cell]; } } } diff --git a/OpenRA.Mods.RA/Buildings/LaysTerrain.cs b/OpenRA.Mods.RA/Buildings/LaysTerrain.cs index 02e3391b4c..d375de521a 100755 --- a/OpenRA.Mods.RA/Buildings/LaysTerrain.cs +++ b/OpenRA.Mods.RA/Buildings/LaysTerrain.cs @@ -57,11 +57,11 @@ namespace OpenRA.Mods.RA.Buildings continue; // Don't place under other buildings or custom terrain - if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c.X, c.Y] != -1) + if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c] != -1) continue; var index = Game.CosmeticRandom.Next(template.TilesCount); - layer.AddTile(c, new TileReference(template.Id, (byte)index)); + layer.AddTile(c, new TerrainTile(template.Id, (byte)index)); } return; @@ -77,10 +77,10 @@ namespace OpenRA.Mods.RA.Buildings continue; // Don't place under other buildings or custom terrain - if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c.X, c.Y] != -1) + if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c] != -1) continue; - layer.AddTile(c, new TileReference(template.Id, (byte)i)); + layer.AddTile(c, new TerrainTile(template.Id, (byte)i)); } } } diff --git a/OpenRA.Mods.RA/Buildings/Util.cs b/OpenRA.Mods.RA/Buildings/Util.cs index aca297f34d..89bb4d2923 100644 --- a/OpenRA.Mods.RA/Buildings/Util.cs +++ b/OpenRA.Mods.RA/Buildings/Util.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA.Buildings if (world.WorldActor.Trait().GetBuildingAt(a) != null) return false; if (world.ActorMap.GetUnitsAt(a).Any(b => b != toIgnore)) return false; - return world.Map.IsInMap(a) && bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type); + return world.Map.Contains(a) && bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type); } public static bool CanPlaceBuilding(this World world, string name, BuildingInfo building, CPos topLeft, Actor toIgnore) @@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Buildings var res = world.WorldActor.Trait(); return FootprintUtils.Tiles(world.Map.Rules, name, building, topLeft).All( - t => world.Map.IsInMap(t.X, t.Y) && res.GetResource(t) == null && + t => world.Map.Contains(t) && res.GetResource(t) == null && world.IsCellBuildable(t, building, toIgnore)); } diff --git a/OpenRA.Mods.RA/Combat.cs b/OpenRA.Mods.RA/Combat.cs index 85434ef1d6..ad28c5c17a 100644 --- a/OpenRA.Mods.RA/Combat.cs +++ b/OpenRA.Mods.RA/Combat.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA var world = firedBy.World; var targetTile = pos.ToCPos(); - if (!world.Map.IsInMap(targetTile)) + if (!world.Map.Contains(targetTile)) return; var isWater = pos.Z <= 0 && world.Map.GetTerrainInfo(targetTile).IsWater; diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index fa8cdd58a6..09484ef770 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -90,7 +90,7 @@ namespace OpenRA.Mods.RA public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors) { - if (!self.World.Map.IsInMap(cell.X, cell.Y)) return false; + if (!self.World.Map.Contains(cell)) return false; var type = self.World.Map.GetTerrainInfo(cell).Type; if (!info.TerrainTypes.Contains(type)) diff --git a/OpenRA.Mods.RA/Effects/Missile.cs b/OpenRA.Mods.RA/Effects/Missile.cs index 8120e0bcca..1404b41639 100755 --- a/OpenRA.Mods.RA/Effects/Missile.cs +++ b/OpenRA.Mods.RA/Effects/Missile.cs @@ -160,7 +160,7 @@ namespace OpenRA.Mods.RA.Effects || (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) // Within range || (info.RangeLimit != 0 && ticks > info.RangeLimit) // Ran out of fuel || (!info.High && world.ActorMap.GetUnitsAt(cell).Any(a => a.HasTrait())) // Hit a wall - || !world.Map.IsInMap(cell) // This also avoids an IndexOutOfRangeException in GetTerrainInfo below. + || !world.Map.Contains(cell) // This also avoids an IndexOutOfRangeException in GetTerrainInfo below. || (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType); // Hit incompatible terrain if (shouldExplode) diff --git a/OpenRA.Mods.RA/Husk.cs b/OpenRA.Mods.RA/Husk.cs index 1606c1c3f7..1fc5e813d9 100644 --- a/OpenRA.Mods.RA/Husk.cs +++ b/OpenRA.Mods.RA/Husk.cs @@ -54,7 +54,7 @@ namespace OpenRA.Mods.RA public IEnumerable> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); } public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors) { - if (!self.World.Map.IsInMap(cell.X, cell.Y)) + if (!self.World.Map.Contains(cell)) return false; if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type)) diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index 86bbf6f5d0..88d642bd5a 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -202,7 +202,7 @@ namespace OpenRA.Mods.RA return false; var location = target.CenterPosition.ToCPos(); - if (!self.World.Map.IsInMap(location)) + if (!self.World.Map.Contains(location)) return false; cursor = "ability"; diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 6ede32679a..fcb964c020 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -101,7 +101,7 @@ namespace OpenRA.Mods.RA.Move public int MovementCostForCell(World world, CPos cell) { - if (!world.Map.IsInMap(cell.X, cell.Y)) + if (!world.Map.Contains(cell)) return int.MaxValue; var index = world.Map.GetTerrainIndex(cell); @@ -580,7 +580,7 @@ namespace OpenRA.Mods.RA.Move if (self.Owner.Shroud.IsExplored(location)) cursor = self.World.Map.GetTerrainInfo(location).CustomCursor ?? cursor; - if (!self.World.Map.IsInMap(location) || (self.Owner.Shroud.IsExplored(location) && + if (!self.World.Map.Contains(location) || (self.Owner.Shroud.IsExplored(location) && unitType.MovementCostForCell(self.World, location) == int.MaxValue)) cursor = "move-blocked"; diff --git a/OpenRA.Mods.RA/Move/PathFinder.cs b/OpenRA.Mods.RA/Move/PathFinder.cs old mode 100755 new mode 100644 index bbf4a3ad39..444fdaae57 --- a/OpenRA.Mods.RA/Move/PathFinder.cs +++ b/OpenRA.Mods.RA/Move/PathFinder.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using OpenRA; using OpenRA.Primitives; using OpenRA.Support; using OpenRA.Traits; @@ -68,7 +69,7 @@ namespace OpenRA.Mods.RA.Move var pb = FindBidiPath( PathSearch.FromPoint(world, mi, self, target, from, true), - PathSearch.FromPoint(world, mi, self, from, target, true).InReverse() + PathSearch.FromPoint(world, mi, self, from, target, true).Reverse() ); CheckSanePath2(pb, from, target); @@ -109,7 +110,7 @@ namespace OpenRA.Mods.RA.Move var path = FindBidiPath( PathSearch.FromPoints(world, mi, self, tilesInRange, src, true), - PathSearch.FromPoint(world, mi, self, src, targetCell, true).InReverse() + PathSearch.FromPoint(world, mi, self, src, targetCell, true).Reverse() ); return path; @@ -124,21 +125,19 @@ namespace OpenRA.Mods.RA.Move { List path = null; - while (!search.queue.Empty) + while (!search.Queue.Empty) { var p = search.Expand(world); - if (search.heuristic(p) == 0) + if (search.Heuristic(p) == 0) { - path = MakePath(search.cellInfo, p); + path = MakePath(search.CellInfo, p); break; } } var dbg = world.WorldActor.TraitOrDefault(); if (dbg != null) - { - dbg.AddLayer(search.considered.Select(p => new Pair(p, search.cellInfo[p.X, p.Y].MinCost)), search.maxCost, search.owner); - } + dbg.AddLayer(search.Considered.Select(p => new Pair(p, search.CellInfo[p].MinCost)), search.MaxCost, search.Owner); if (path != null) return path; @@ -149,15 +148,15 @@ namespace OpenRA.Mods.RA.Move } } - static List MakePath(CellInfo[,] cellInfo, CPos destination) + static List MakePath(CellLayer cellInfo, CPos destination) { var ret = new List(); var pathNode = destination; - while (cellInfo[pathNode.X, pathNode.Y].Path != pathNode) + while (cellInfo[pathNode].Path != pathNode) { ret.Add(pathNode); - pathNode = cellInfo[pathNode.X, pathNode.Y].Path; + pathNode = cellInfo[pathNode].Path; } ret.Add(pathNode); @@ -165,9 +164,8 @@ namespace OpenRA.Mods.RA.Move return ret; } - public List FindBidiPath( /* searches from both ends toward each other */ - PathSearch fromSrc, - PathSearch fromDest) + // Searches from both ends toward each other + public List FindBidiPath(PathSearch fromSrc, PathSearch fromDest) { using (new PerfSample("Pathfinder")) { @@ -176,13 +174,13 @@ namespace OpenRA.Mods.RA.Move { List path = null; - while (!fromSrc.queue.Empty && !fromDest.queue.Empty) + while (!fromSrc.Queue.Empty && !fromDest.Queue.Empty) { /* make some progress on the first search */ var p = fromSrc.Expand(world); - if (fromDest.cellInfo[p.X, p.Y].Seen && - fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity) + if (fromDest.CellInfo[p].Seen && + fromDest.CellInfo[p].MinCost < float.PositiveInfinity) { path = MakeBidiPath(fromSrc, fromDest, p); break; @@ -191,8 +189,8 @@ namespace OpenRA.Mods.RA.Move /* make some progress on the second search */ var q = fromDest.Expand(world); - if (fromSrc.cellInfo[q.X, q.Y].Seen && - fromSrc.cellInfo[q.X, q.Y].MinCost < float.PositiveInfinity) + if (fromSrc.CellInfo[q].Seen && + fromSrc.CellInfo[q].MinCost < float.PositiveInfinity) { path = MakeBidiPath(fromSrc, fromDest, q); break; @@ -202,8 +200,8 @@ namespace OpenRA.Mods.RA.Move var dbg = world.WorldActor.TraitOrDefault(); if (dbg != null) { - dbg.AddLayer(fromSrc.considered.Select(p => new Pair(p, fromSrc.cellInfo[p.X, p.Y].MinCost)), fromSrc.maxCost, fromSrc.owner); - dbg.AddLayer(fromDest.considered.Select(p => new Pair(p, fromDest.cellInfo[p.X, p.Y].MinCost)), fromDest.maxCost, fromDest.owner); + dbg.AddLayer(fromSrc.Considered.Select(p => new Pair(p, fromSrc.CellInfo[p].MinCost)), fromSrc.MaxCost, fromSrc.Owner); + dbg.AddLayer(fromDest.Considered.Select(p => new Pair(p, fromDest.CellInfo[p].MinCost)), fromDest.MaxCost, fromDest.Owner); } if (path != null) @@ -216,25 +214,25 @@ namespace OpenRA.Mods.RA.Move static List MakeBidiPath(PathSearch a, PathSearch b, CPos p) { - var ca = a.cellInfo; - var cb = b.cellInfo; + var ca = a.CellInfo; + var cb = b.CellInfo; var ret = new List(); var q = p; - while (ca[q.X, q.Y].Path != q) + while (ca[q].Path != q) { ret.Add(q); - q = ca[q.X, q.Y].Path; + q = ca[q].Path; } ret.Add(q); ret.Reverse(); q = p; - while (cb[q.X, q.Y].Path != q) + while (cb[q].Path != q) { - q = cb[q.X, q.Y].Path; + q = cb[q].Path; ret.Add(q); } @@ -286,8 +284,8 @@ namespace OpenRA.Mods.RA.Move public struct PathDistance : IComparable { - public int EstTotal; - public CPos Location; + public readonly int EstTotal; + public readonly CPos Location; public PathDistance(int estTotal, CPos location) { diff --git a/OpenRA.Mods.RA/Move/PathSearch.cs b/OpenRA.Mods.RA/Move/PathSearch.cs index b453a00a26..1b61a86814 100755 --- a/OpenRA.Mods.RA/Move/PathSearch.cs +++ b/OpenRA.Mods.RA/Move/PathSearch.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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, @@ -10,45 +10,97 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; +using OpenRA; using OpenRA.Primitives; namespace OpenRA.Mods.RA.Move { public sealed class PathSearch : IDisposable { - World world; - public CellInfo[,] cellInfo; - public PriorityQueue queue; - public Func heuristic; + public CellLayer CellInfo; + public PriorityQueue Queue; + public Func Heuristic; + public bool CheckForBlocked; + public Actor IgnoreBuilding; + public bool InReverse; + public HashSet Considered; + public Player Owner { get { return self.Owner; } } + public int MaxCost; + + Actor self; + MobileInfo mobileInfo; Func customCost; Func customBlock; - public bool checkForBlocked; - public Actor ignoreBuilding; - public bool inReverse; - public HashSet considered; - public int maxCost; Pair[] nextDirections; - MobileInfo mobileInfo; - Actor self; - public Player owner { get { return self.Owner; } } + int laneBias = 1; public PathSearch(World world, MobileInfo mobileInfo, Actor self) { - this.world = world; - cellInfo = InitCellInfo(); + this.self = self; + CellInfo = InitCellInfo(); this.mobileInfo = mobileInfo; this.self = self; customCost = null; - queue = new PriorityQueue(); - considered = new HashSet(); - maxCost = 0; + Queue = new PriorityQueue(); + Considered = new HashSet(); + MaxCost = 0; nextDirections = CVec.directions.Select(d => new Pair(d, 0)).ToArray(); } - public PathSearch InReverse() + public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked) { - inReverse = true; + var search = new PathSearch(world, mi, self) + { + CheckForBlocked = checkForBlocked + }; + + return search; + } + + public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked) + { + var search = new PathSearch(world, mi, self) + { + Heuristic = DefaultEstimator(target), + CheckForBlocked = checkForBlocked + }; + + search.AddInitialCell(from); + return search; + } + + public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable froms, CPos target, bool checkForBlocked) + { + var search = new PathSearch(world, mi, self) + { + Heuristic = DefaultEstimator(target), + CheckForBlocked = checkForBlocked + }; + + foreach (var sl in froms) + search.AddInitialCell(sl); + + return search; + } + + public static Func DefaultEstimator(CPos destination) + { + return here => + { + var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y)); + var straight = Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y); + + // HACK: this relies on fp and cell-size assumptions. + var h = (3400 * diag / 24) + 100 * (straight - (2 * diag)); + return (int)(h * 1.001); + }; + } + + public PathSearch Reverse() + { + InReverse = true; return this; } @@ -60,13 +112,13 @@ namespace OpenRA.Mods.RA.Move public PathSearch WithIgnoredBuilding(Actor b) { - ignoreBuilding = b; + IgnoreBuilding = b; return this; } public PathSearch WithHeuristic(Func h) { - heuristic = h; + Heuristic = h; return this; } @@ -78,7 +130,7 @@ namespace OpenRA.Mods.RA.Move public PathSearch WithoutLaneBias() { - LaneBias = 0; + laneBias = 0; return this; } @@ -88,18 +140,20 @@ namespace OpenRA.Mods.RA.Move return this; } - int LaneBias = 1; - public CPos Expand(World world) { - var p = queue.Pop(); - while (cellInfo[p.Location.X, p.Location.Y].Seen) - if (queue.Empty) + var p = Queue.Pop(); + while (CellInfo[p.Location].Seen) + { + if (Queue.Empty) return p.Location; - else - p = queue.Pop(); - cellInfo[p.Location.X, p.Location.Y].Seen = true; + p = Queue.Pop(); + } + + var pCell = CellInfo[p.Location]; + pCell.Seen = true; + CellInfo[p.Location] = pCell; var thisCost = mobileInfo.MovementCostForCell(world, p.Location); @@ -114,7 +168,7 @@ namespace OpenRA.Mods.RA.Move } // This current cell is ok; check all immediate directions: - considered.Add(p.Location); + Considered.Add(p.Location); for (var i = 0; i < nextDirections.Length; ++i) { @@ -124,9 +178,10 @@ namespace OpenRA.Mods.RA.Move var newHere = p.Location + d; // Is this direction flat-out unusable or already seen? - if (!world.Map.IsInMap(newHere.X, newHere.Y)) + if (!world.Map.Contains(newHere)) continue; - if (cellInfo[newHere.X, newHere.Y].Seen) + + if (CellInfo[newHere].Seen) continue; // Now we may seriously consider this direction using heuristics: @@ -135,18 +190,19 @@ namespace OpenRA.Mods.RA.Move if (costHere == int.MaxValue) continue; - if (!mobileInfo.CanEnterCell(world, self, newHere, ignoreBuilding, checkForBlocked, false)) + if (!mobileInfo.CanEnterCell(world, self, newHere, IgnoreBuilding, CheckForBlocked, false)) continue; if (customBlock != null && customBlock(newHere)) continue; - var est = heuristic(newHere); + var est = Heuristic(newHere); if (est == int.MaxValue) continue; var cellCost = costHere; - if (d.X * d.Y != 0) cellCost = (cellCost * 34) / 24; + if (d.X * d.Y != 0) + cellCost = (cellCost * 34) / 24; var userCost = 0; if (customCost != null) @@ -156,105 +212,82 @@ namespace OpenRA.Mods.RA.Move } // directional bonuses for smoother flow! - if (LaneBias != 0) + if (laneBias != 0) { - var ux = (newHere.X + (inReverse ? 1 : 0) & 1); - var uy = (newHere.Y + (inReverse ? 1 : 0) & 1); + var ux = newHere.X + (InReverse ? 1 : 0) & 1; + var uy = newHere.Y + (InReverse ? 1 : 0) & 1; - if (ux == 0 && d.Y < 0) cellCost += LaneBias; - else if (ux == 1 && d.Y > 0) cellCost += LaneBias; - if (uy == 0 && d.X < 0) cellCost += LaneBias; - else if (uy == 1 && d.X > 0) cellCost += LaneBias; + if (ux == 0 && d.Y < 0) + cellCost += laneBias; + else if (ux == 1 && d.Y > 0) + cellCost += laneBias; + + if (uy == 0 && d.X < 0) + cellCost += laneBias; + else if (uy == 1 && d.X > 0) + cellCost += laneBias; } - var newCost = cellInfo[p.Location.X, p.Location.Y].MinCost + cellCost; + var newCost = CellInfo[p.Location].MinCost + cellCost; // Cost is even higher; next direction: - if (newCost > cellInfo[newHere.X, newHere.Y].MinCost) + if (newCost > CellInfo[newHere].MinCost) continue; - cellInfo[newHere.X, newHere.Y].Path = p.Location; - cellInfo[newHere.X, newHere.Y].MinCost = newCost; + var hereCell = CellInfo[newHere]; + hereCell.Path = p.Location; + hereCell.MinCost = newCost; + CellInfo[newHere] = hereCell; nextDirections[i].Second = newCost + est; - queue.Add(new PathDistance(newCost + est, newHere)); + Queue.Add(new PathDistance(newCost + est, newHere)); - if (newCost > maxCost) maxCost = newCost; - considered.Add(newHere); + if (newCost > MaxCost) + MaxCost = newCost; + + Considered.Add(newHere); } // Sort to prefer the cheaper direction: - //Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second)); - + // Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second)); return p.Location; } public void AddInitialCell(CPos location) { - if (!world.Map.IsInMap(location.X, location.Y)) + if (!self.World.Map.Contains(location)) return; - cellInfo[location.X, location.Y] = new CellInfo(0, location, false); - queue.Add(new PathDistance(heuristic(location), location)); + CellInfo[location] = new CellInfo(0, location, false); + Queue.Add(new PathDistance(Heuristic(location), location)); } - public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked) + static readonly Queue> CellInfoPool = new Queue>(); + + static CellLayer GetFromPool() { - var search = new PathSearch(world, mi, self) - { - checkForBlocked = checkForBlocked - }; - return search; + lock (CellInfoPool) + return CellInfoPool.Dequeue(); } - public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked) + static void PutBackIntoPool(CellLayer ci) { - var search = new PathSearch(world, mi, self) - { - heuristic = DefaultEstimator(target), - checkForBlocked = checkForBlocked - }; - - search.AddInitialCell(from); - return search; + lock (CellInfoPool) + CellInfoPool.Enqueue(ci); } - public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable froms, CPos target, bool checkForBlocked) + CellLayer InitCellInfo() { - var search = new PathSearch(world, mi, self) - { - heuristic = DefaultEstimator(target), - checkForBlocked = checkForBlocked - }; + CellLayer result = null; + var mapSize = new Size(self.World.Map.MapSize.X, self.World.Map.MapSize.Y); - foreach (var sl in froms) - search.AddInitialCell(sl); - - return search; - } - - static readonly Queue cellInfoPool = new Queue(); - - static CellInfo[,] GetFromPool() - { - lock (cellInfoPool) - return cellInfoPool.Dequeue(); - } - - static void PutBackIntoPool(CellInfo[,] ci) - { - lock (cellInfoPool) - cellInfoPool.Enqueue(ci); - } - - CellInfo[,] InitCellInfo() - { - CellInfo[,] result = null; - while (cellInfoPool.Count > 0) + // HACK: Uses a static cache so that double-ended searches (which have two PathSearch instances) + // can implicitly share data. The PathFinder should allocate the CellInfo array and pass it + // explicitly to the things that need to share it. + while (CellInfoPool.Count > 0) { var cellInfo = GetFromPool(); - if (cellInfo.GetUpperBound(0) != world.Map.MapSize.X - 1 || - cellInfo.GetUpperBound(1) != world.Map.MapSize.Y - 1) + if (cellInfo.Size != mapSize) { Log.Write("debug", "Discarding old pooled CellInfo of wrong size."); continue; @@ -265,36 +298,24 @@ namespace OpenRA.Mods.RA.Move } if (result == null) - result = new CellInfo[world.Map.MapSize.X, world.Map.MapSize.Y]; + result = new CellLayer(self.World.Map); - for (var x = 0; x < world.Map.MapSize.X; x++) - for (var y = 0; y < world.Map.MapSize.Y; y++) - result[ x, y ] = new CellInfo( int.MaxValue, new CPos( x, y ), false ); + foreach (var cell in self.World.Map.Cells) + result[cell] = new CellInfo(int.MaxValue, cell, false); return result; } - public static Func DefaultEstimator(CPos destination) - { - return here => - { - var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y)); - var straight = (Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y)); - var h = (3400 * diag / 24) + 100 * (straight - (2 * diag)); - h = (int)(h * 1.001); - return h; - }; - } - bool disposed; public void Dispose() { if (disposed) return; + disposed = true; - PutBackIntoPool(cellInfo); - cellInfo = null; + PutBackIntoPool(CellInfo); + CellInfo = null; GC.SuppressFinalize(this); } diff --git a/OpenRA.Mods.RA/RallyPoint.cs b/OpenRA.Mods.RA/RallyPoint.cs index 2fcb3f660d..3257659ab6 100755 --- a/OpenRA.Mods.RA/RallyPoint.cs +++ b/OpenRA.Mods.RA/RallyPoint.cs @@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA return false; var location = target.CenterPosition.ToCPos(); - if (self.World.Map.IsInMap(location)) + if (self.World.Map.Contains(location)) { cursor = "ability"; return true; diff --git a/OpenRA.Mods.RA/Render/RenderLandingCraft.cs b/OpenRA.Mods.RA/Render/RenderLandingCraft.cs index 4203ed44b1..100c5a4a3f 100644 --- a/OpenRA.Mods.RA/Render/RenderLandingCraft.cs +++ b/OpenRA.Mods.RA/Render/RenderLandingCraft.cs @@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Render if (self.CenterPosition.Z > 0 || move.IsMoving) return false; - return cargo.CurrentAdjacentCells.Any(c => self.World.Map.IsInMap(c) + return cargo.CurrentAdjacentCells.Any(c => self.World.Map.Contains(c) && info.OpenTerrainTypes.Contains(self.World.Map.GetTerrainInfo(c).Type)); } diff --git a/OpenRA.Mods.RA/ShroudRenderer.cs b/OpenRA.Mods.RA/ShroudRenderer.cs index d7ac87c25f..433a229b7e 100644 --- a/OpenRA.Mods.RA/ShroudRenderer.cs +++ b/OpenRA.Mods.RA/ShroudRenderer.cs @@ -10,6 +10,7 @@ using System.Drawing; using System.Linq; +using OpenRA; using OpenRA.Graphics; using OpenRA.Traits; @@ -35,36 +36,41 @@ namespace OpenRA.Mods.RA public class ShroudRenderer : IRenderShroud, IWorldLoaded { - struct ShroudTile + class ShroudTile { - public CPos Position; - public float2 ScreenPosition; - public int Variant; + public readonly CPos Position; + public readonly float2 ScreenPosition; + public readonly int Variant; public Sprite Fog; public Sprite Shroud; + + public ShroudTile(CPos position, float2 screenPosition, int variant) + { + Position = position; + ScreenPosition = screenPosition; + Variant = variant; + } } + ShroudRendererInfo info; Sprite[] sprites; Sprite unexploredTile; int[] spriteMap; - ShroudTile[] tiles; - int tileStride, variantStride; + CellLayer tiles; + int variantStride; int shroudHash; PaletteReference fogPalette, shroudPalette; - Rectangle bounds; - bool useExtendedIndex; + Map map; public ShroudRenderer(World world, ShroudRendererInfo info) { - var map = world.Map; - bounds = map.Bounds; - useExtendedIndex = info.UseExtendedIndex; + this.info = info; + map = world.Map; - tiles = new ShroudTile[map.MapSize.X * map.MapSize.Y]; - tileStride = map.MapSize.X; + tiles = new CellLayer(map); // Force update on first render shroudHash = -1; @@ -80,14 +86,10 @@ namespace OpenRA.Mods.RA } // Mapping of shrouded directions -> sprite index - spriteMap = new int[useExtendedIndex ? 256 : 16]; + spriteMap = new int[info.UseExtendedIndex ? 256 : 16]; for (var i = 0; i < info.Index.Length; i++) spriteMap[info.Index[i]] = i; - // Set individual tile variants to reduce tiling - for (var i = 0; i < tiles.Length; i++) - tiles[i].Variant = Game.CosmeticRandom.Next(info.Variants.Length); - // Synthesize unexplored tile if it isn't defined if (!info.Index.Contains(0)) { @@ -102,21 +104,21 @@ namespace OpenRA.Mods.RA static int FoggedEdges(Shroud s, CPos p, bool useExtendedIndex) { - if (!s.IsVisible(p.X, p.Y)) + if (!s.IsVisible(p)) return 15; // If a side is shrouded then we also count the corners var u = 0; - if (!s.IsVisible(p.X, p.Y - 1)) u |= 0x13; - if (!s.IsVisible(p.X + 1, p.Y)) u |= 0x26; - if (!s.IsVisible(p.X, p.Y + 1)) u |= 0x4C; - if (!s.IsVisible(p.X - 1, p.Y)) u |= 0x89; + if (!s.IsVisible(p + new CVec(0, -1))) u |= 0x13; + if (!s.IsVisible(p + new CVec(1, 0))) u |= 0x26; + if (!s.IsVisible(p + new CVec(0, 1))) u |= 0x4C; + if (!s.IsVisible(p + new CVec(-1, 0))) u |= 0x89; var uside = u & 0x0F; - if (!s.IsVisible(p.X - 1, p.Y - 1)) u |= 0x01; - if (!s.IsVisible(p.X + 1, p.Y - 1)) u |= 0x02; - if (!s.IsVisible(p.X + 1, p.Y + 1)) u |= 0x04; - if (!s.IsVisible(p.X - 1, p.Y + 1)) u |= 0x08; + if (!s.IsVisible(p + new CVec(-1, -1))) u |= 0x01; + if (!s.IsVisible(p + new CVec(1, -1))) u |= 0x02; + if (!s.IsVisible(p + new CVec(1, 1))) u |= 0x04; + if (!s.IsVisible(p + new CVec(-1, 1))) u |= 0x08; // RA provides a set of frames for tiles with shrouded // corners but unshrouded edges. We want to detect this @@ -129,21 +131,21 @@ namespace OpenRA.Mods.RA static int ShroudedEdges(Shroud s, CPos p, bool useExtendedIndex) { - if (!s.IsExplored(p.X, p.Y)) + if (!s.IsExplored(p)) return 15; // If a side is shrouded then we also count the corners var u = 0; - if (!s.IsExplored(p.X, p.Y - 1)) u |= 0x13; - if (!s.IsExplored(p.X + 1, p.Y)) u |= 0x26; - if (!s.IsExplored(p.X, p.Y + 1)) u |= 0x4C; - if (!s.IsExplored(p.X - 1, p.Y)) u |= 0x89; + if (!s.IsExplored(p + new CVec(0, -1))) u |= 0x13; + if (!s.IsExplored(p + new CVec(1, 0))) u |= 0x26; + if (!s.IsExplored(p + new CVec(0, 1))) u |= 0x4C; + if (!s.IsExplored(p + new CVec(-1, 0))) u |= 0x89; var uside = u & 0x0F; - if (!s.IsExplored(p.X - 1, p.Y - 1)) u |= 0x01; - if (!s.IsExplored(p.X + 1, p.Y - 1)) u |= 0x02; - if (!s.IsExplored(p.X + 1, p.Y + 1)) u |= 0x04; - if (!s.IsExplored(p.X - 1, p.Y + 1)) u |= 0x08; + if (!s.IsExplored(p + new CVec(-1, -1))) u |= 0x01; + if (!s.IsExplored(p + new CVec(1, -1))) u |= 0x02; + if (!s.IsExplored(p + new CVec(1, 1))) u |= 0x04; + if (!s.IsExplored(p + new CVec(-1, 1))) u |= 0x08; // RA provides a set of frames for tiles with shrouded // corners but unshrouded edges. We want to detect this @@ -173,15 +175,12 @@ namespace OpenRA.Mods.RA public void WorldLoaded(World w, WorldRenderer wr) { - // Cache the tile positions to avoid unnecessary calculations - for (var i = bounds.Left; i < bounds.Right; i++) + // Initialize tile cache + foreach (var cell in map.Cells) { - for (var j = bounds.Top; j < bounds.Bottom; j++) - { - var k = j * tileStride + i; - tiles[k].Position = new CPos(i, j); - tiles[k].ScreenPosition = wr.ScreenPosition(tiles[k].Position.CenterPosition); - } + var screen = wr.ScreenPosition(cell.CenterPosition); + var variant = Game.CosmeticRandom.Next(info.Variants.Length); + tiles[cell] = new ShroudTile(cell, screen, variant); } fogPalette = wr.Palette("fog"); @@ -209,22 +208,25 @@ namespace OpenRA.Mods.RA if (shroud == null) { // Players with no shroud see the whole map so we only need to set the edges - for (var k = 0; k < tiles.Length; k++) + foreach (var cell in map.Cells) { - var shrouded = ObserverShroudedEdges(tiles[k].Position, bounds, useExtendedIndex); - tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant); - tiles[k].Fog = GetTile(shrouded, tiles[k].Variant); + var t = tiles[cell]; + var shrouded = ObserverShroudedEdges(t.Position, map.Bounds, info.UseExtendedIndex); + + t.Shroud = GetTile(shrouded, t.Variant); + t.Fog = GetTile(shrouded, t.Variant); } } else { - for (var k = 0; k < tiles.Length; k++) + foreach (var cell in map.Cells) { - var shrouded = ShroudedEdges(shroud, tiles[k].Position, useExtendedIndex); - var fogged = FoggedEdges(shroud, tiles[k].Position, useExtendedIndex); + var t = tiles[cell]; + var shrouded = ShroudedEdges(shroud, t.Position, info.UseExtendedIndex); + var fogged = FoggedEdges(shroud, t.Position, info.UseExtendedIndex); - tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant); - tiles[k].Fog = GetTile(fogged, tiles[k].Variant); + t.Shroud = GetTile(shrouded, t.Variant); + t.Fog = GetTile(fogged, t.Variant); } } } @@ -233,27 +235,20 @@ namespace OpenRA.Mods.RA { Update(shroud); - var clip = wr.Viewport.CellBounds; - var width = clip.Width; - for (var j = clip.Top; j < clip.Bottom; j++) + foreach (var cell in wr.Viewport.VisibleCells) { - var start = j * tileStride + clip.Left; - for (var k = 0; k < width; k++) + var t = tiles[cell]; + + if (t.Shroud != null) { - var s = tiles[start + k].Shroud; - var f = tiles[start + k].Fog; + var pos = t.ScreenPosition - 0.5f * t.Shroud.size; + Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Shroud, pos, shroudPalette); + } - if (s != null) - { - var pos = tiles[start + k].ScreenPosition - 0.5f * s.size; - Game.Renderer.WorldSpriteRenderer.DrawSprite(s, pos, shroudPalette); - } - - if (f != null) - { - var pos = tiles[start + k].ScreenPosition - 0.5f * f.size; - Game.Renderer.WorldSpriteRenderer.DrawSprite(f, pos, fogPalette); - } + if (t.Fog != null) + { + var pos = t.ScreenPosition - 0.5f * t.Fog.size; + Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Fog, pos, fogPalette); } } } diff --git a/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs index 2220f07b5a..ec7fb92a10 100644 --- a/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs +++ b/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs @@ -245,7 +245,7 @@ namespace OpenRA.Mods.RA public IEnumerable Order(World world, CPos xy, MouseInput mi) { world.CancelInputMode(); - if (mi.Button == expectedButton && world.Map.IsInMap(xy)) + if (mi.Button == expectedButton && world.Map.Contains(xy)) yield return new Order(order, manager.self, false) { TargetLocation = xy, SuppressVisualFeedback = true }; } @@ -258,6 +258,6 @@ namespace OpenRA.Mods.RA public IEnumerable Render(WorldRenderer wr, World world) { yield break; } public void RenderAfterWorld(WorldRenderer wr, World world) { } - public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; } + public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; } } } diff --git a/OpenRA.Mods.RA/World/BuildableTerrainLayer.cs b/OpenRA.Mods.RA/World/BuildableTerrainLayer.cs index 4a07267f9f..e580673c99 100644 --- a/OpenRA.Mods.RA/World/BuildableTerrainLayer.cs +++ b/OpenRA.Mods.RA/World/BuildableTerrainLayer.cs @@ -32,9 +32,9 @@ namespace OpenRA.Mods.RA dirty = new Dictionary(); } - public void AddTile(CPos cell, TileReference tile) + public void AddTile(CPos cell, TerrainTile tile) { - map.CustomTerrain[cell.X, cell.Y] = tileset.GetTerrainIndex(tile); + map.CustomTerrain[cell] = tileset.GetTerrainIndex(tile); // Terrain tiles define their origin at the topleft var s = theater.TileSprite(tile); @@ -59,12 +59,11 @@ namespace OpenRA.Mods.RA public void Render(WorldRenderer wr) { - var cliprect = wr.Viewport.CellBounds; var pal = wr.Palette("terrain"); foreach (var kv in tiles) { - if (!cliprect.Contains(kv.Key.X, kv.Key.Y)) + if (!wr.Viewport.VisibleCells.Contains(kv.Key)) continue; if (wr.world.ShroudObscures(kv.Key)) diff --git a/OpenRA.Mods.RA/World/DomainIndex.cs b/OpenRA.Mods.RA/World/DomainIndex.cs index 2e6dbdbf76..9b6704cca2 100644 --- a/OpenRA.Mods.RA/World/DomainIndex.cs +++ b/OpenRA.Mods.RA/World/DomainIndex.cs @@ -53,17 +53,17 @@ namespace OpenRA.Mods.RA class MovementClassDomainIndex { - Rectangle bounds; + Map map; uint movementClass; - int[,] domains; + CellLayer domains; Dictionary> transientConnections; public MovementClassDomainIndex(World world, uint movementClass) { - bounds = world.Map.Bounds; + map = world.Map; this.movementClass = movementClass; - domains = new int[(bounds.Width + bounds.X), (bounds.Height + bounds.Y)]; + domains = new CellLayer(world.Map); transientConnections = new Dictionary>(); BuildDomains(world); @@ -71,15 +71,15 @@ namespace OpenRA.Mods.RA public bool IsPassable(CPos p1, CPos p2) { - if (!bounds.Contains(p1.X, p1.Y) || !bounds.Contains(p2.X, p2.Y)) + if (!map.Contains(p1) || !map.Contains(p2)) return false; - if (domains[p1.X, p1.Y] == domains[p2.X, p2.Y]) + if (domains[p1] == domains[p2]) return true; // Even though p1 and p2 are in different domains, it's possible // that some dynamic terrain (i.e. bridges) may connect them. - return HasConnection(GetDomainOf(p1), GetDomainOf(p2)); + return HasConnection(domains[p1], domains[p2]); } public void UpdateCells(World world, HashSet dirtyCells) @@ -90,22 +90,21 @@ namespace OpenRA.Mods.RA { // Select all neighbors inside the map boundries var neighbors = CVec.directions.Select(d => d + cell) - .Where(c => bounds.Contains(c.X, c.Y)); + .Where(c => map.Contains(c)); var found = false; - foreach (var neighbor in neighbors) + foreach (var n in neighbors) { - if (!dirtyCells.Contains(neighbor)) + if (!dirtyCells.Contains(n)) { - var neighborDomain = GetDomainOf(neighbor); - - var match = CanTraverseTile(world, neighbor); - if (match) neighborDomains.Add(neighborDomain); + var neighborDomain = domains[n]; + if (CanTraverseTile(world, n)) + neighborDomains.Add(neighborDomain); // Set ourselves to the first non-dirty neighbor we find. if (!found) { - SetDomain(cell, neighborDomain); + domains[cell] = neighborDomain; found = true; } } @@ -113,20 +112,8 @@ namespace OpenRA.Mods.RA } foreach (var c1 in neighborDomains) - { foreach (var c2 in neighborDomains) CreateConnection(c1, c2); - } - } - - int GetDomainOf(CPos p) - { - return domains[p.X, p.Y]; - } - - void SetDomain(CPos p, int domain) - { - domains[p.X, p.Y] = domain; } bool HasConnection(int d1, int d2) @@ -146,6 +133,7 @@ namespace OpenRA.Mods.RA { if (neighbor == d2) return true; + if (!visited.Contains(neighbor)) toProcess.Push(neighbor); } @@ -180,7 +168,7 @@ namespace OpenRA.Mods.RA var domain = 1; - var visited = new bool[(bounds.Width + bounds.X), (bounds.Height + bounds.Y)]; + var visited = new CellLayer(map); var toProcess = new Queue(); toProcess.Enqueue(new CPos(map.Bounds.Left, map.Bounds.Top)); @@ -192,7 +180,7 @@ namespace OpenRA.Mods.RA // Technically redundant with the check in the inner loop, but prevents // ballooning the domain counter. - if (visited[start.X, start.Y]) + if (visited[start]) continue; var domainQueue = new Queue(); @@ -205,7 +193,7 @@ namespace OpenRA.Mods.RA while (domainQueue.Count != 0) { var n = domainQueue.Dequeue(); - if (visited[n.X, n.Y]) + if (visited[n]) continue; var candidatePassable = CanTraverseTile(world, n); @@ -215,12 +203,12 @@ namespace OpenRA.Mods.RA continue; } - visited[n.X, n.Y] = true; - SetDomain(n, domain); + visited[n] = true; + domains[n] = domain; // Don't crawl off the map, or add already-visited cells var neighbors = CVec.directions.Select(d => n + d) - .Where(p => bounds.Contains(p.X, p.Y) && !visited[p.X, p.Y]); + .Where(p => map.Contains(p) && !visited[p]); foreach (var neighbor in neighbors) domainQueue.Enqueue(neighbor); diff --git a/OpenRA.Mods.RA/World/PathfinderDebugOverlay.cs b/OpenRA.Mods.RA/World/PathfinderDebugOverlay.cs index ba6df9ce47..17cdcd7d50 100644 --- a/OpenRA.Mods.RA/World/PathfinderDebugOverlay.cs +++ b/OpenRA.Mods.RA/World/PathfinderDebugOverlay.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using OpenRA; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -20,7 +21,7 @@ namespace OpenRA.Mods.RA class PathfinderDebugOverlayInfo : TraitInfo { } class PathfinderDebugOverlay : IRenderOverlay, IWorldLoaded { - Dictionary layers; + Dictionary> layers; int refreshTick; World world; public bool Visible; @@ -29,7 +30,7 @@ namespace OpenRA.Mods.RA { world = w; refreshTick = 0; - layers = new Dictionary(8); + layers = new Dictionary>(8); // Enabled via Cheats menu Visible = false; @@ -39,15 +40,15 @@ namespace OpenRA.Mods.RA { if (maxWeight == 0) return; - int[,] layer; + CellLayer layer; if (!layers.TryGetValue(pl, out layer)) { - layer = new int[world.Map.MapSize.X, world.Map.MapSize.Y]; + layer = new CellLayer(world.Map); layers.Add(pl, layer); } foreach (var p in cellWeights) - layer[p.First.X, p.First.Y] = Math.Min(128, layer[p.First.X, p.First.Y] + (maxWeight - p.Second) * 64 / maxWeight); + layer[p.First] = Math.Min(128, layer[p.First] + (maxWeight - p.Second) * 64 / maxWeight); } public void Render(WorldRenderer wr) @@ -59,29 +60,25 @@ namespace OpenRA.Mods.RA var doDim = refreshTick - world.WorldTick <= 0; if (doDim) refreshTick = world.WorldTick + 20; - var viewBounds = wr.Viewport.CellBounds; foreach (var pair in layers) { var c = (pair.Key != null) ? pair.Key.Color.RGB : Color.PaleTurquoise; var layer = pair.Value; // Only render quads in viewing range: - for (var j = viewBounds.Top; j <= viewBounds.Bottom; ++j) + foreach (var cell in wr.Viewport.VisibleCells) { - for (var i = viewBounds.Left; i <= viewBounds.Right; ++i) - { - if (layer[i, j] <= 0) - continue; + if (layer[cell] <= 0) + continue; - var w = Math.Max(0, Math.Min(layer[i, j], 128)); - if (doDim) - layer[i, j] = layer[i, j] * 5 / 6; + var w = Math.Max(0, Math.Min(layer[cell], 128)); + if (doDim) + layer[cell] = layer[cell] * 5 / 6; - // TODO: This doesn't make sense for isometric terrain - var tl = wr.ScreenPxPosition(new CPos(i, j).TopLeft); - var br = wr.ScreenPxPosition(new CPos(i, j).BottomRight); - qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c)); - } + // TODO: This doesn't make sense for isometric terrain + var tl = wr.ScreenPxPosition(cell.TopLeft); + var br = wr.ScreenPxPosition(cell.BottomRight); + qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c)); } } } diff --git a/OpenRA.Mods.RA/World/SmudgeLayer.cs b/OpenRA.Mods.RA/World/SmudgeLayer.cs index a632b44379..f1c1e65e35 100644 --- a/OpenRA.Mods.RA/World/SmudgeLayer.cs +++ b/OpenRA.Mods.RA/World/SmudgeLayer.cs @@ -120,12 +120,11 @@ namespace OpenRA.Mods.RA public void Render(WorldRenderer wr) { - var cliprect = wr.Viewport.CellBounds; var pal = wr.Palette("terrain"); foreach (var kv in tiles) { - if (!cliprect.Contains(kv.Key.X, kv.Key.Y)) + if (!wr.Viewport.VisibleCells.Contains(kv.Key)) continue; if (world.ShroudObscures(kv.Key)) diff --git a/OpenRA.Utility/LegacyMapImporter.cs b/OpenRA.Utility/LegacyMapImporter.cs index e1a5aa359e..ded0b9d2ab 100644 --- a/OpenRA.Utility/LegacyMapImporter.cs +++ b/OpenRA.Utility/LegacyMapImporter.cs @@ -137,6 +137,7 @@ namespace OpenRA.Utility var width = Exts.ParseIntegerInvariant(mapSection.GetValue("Width", "0")); var height = Exts.ParseIntegerInvariant(mapSection.GetValue("Height", "0")); mapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64; + var size = new Size(mapSize, mapSize); map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(iniFile)); map.Author = "Westwood Studios"; @@ -148,8 +149,8 @@ namespace OpenRA.Utility map.Smudges = Exts.Lazy(() => new List()); map.Actors = Exts.Lazy(() => new Dictionary()); - map.MapResources = Exts.Lazy(() => new TileReference[mapSize, mapSize]); - map.MapTiles = Exts.Lazy(() => new TileReference[mapSize, mapSize]); + map.MapResources = Exts.Lazy(() => new CellLayer(size)); + map.MapTiles = Exts.Lazy(() => new CellLayer(size)); map.Options = new MapOptions(); @@ -250,20 +251,20 @@ namespace OpenRA.Utility void UnpackRATileData(MemoryStream ms) { - for (var i = 0; i < mapSize; i++) - for (var j = 0; j < mapSize; j++) - map.MapTiles.Value[i, j] = new TileReference(); - + var types = new ushort[mapSize, mapSize]; for (var j = 0; j < mapSize; j++) + { for (var i = 0; i < mapSize; i++) { var tileID = ms.ReadUInt16(); - map.MapTiles.Value[i, j].Type = tileID == (ushort)0 ? (ushort)255 : tileID; // RAED weirdness + types[i, j] = tileID == (ushort)0 ? (ushort)255 : tileID; // RAED weirdness } + } for (var j = 0; j < mapSize; j++) for (var i = 0; i < mapSize; i++) - map.MapTiles.Value[i, j].Index = ms.ReadUInt8(); + map.MapTiles.Value[new CPos(i, j)] = new TerrainTile(types[i, j], ms.ReadUInt8()); + } void UnpackRAOverlayData(MemoryStream ms) @@ -277,15 +278,16 @@ namespace OpenRA.Utility if (o != 255 && overlayResourceMapping.ContainsKey(redAlertOverlayNames[o])) res = overlayResourceMapping[redAlertOverlayNames[o]]; - - map.MapResources.Value[i, j] = new TileReference(res.First, res.Second); + + var cell = new CPos(i, j); + map.MapResources.Value[cell] = new ResourceTile(res.First, res.Second); if (o != 255 && overlayActorMapping.ContainsKey(redAlertOverlayNames[o])) { map.Actors.Value.Add("Actor" + actorCount++, new ActorReference(overlayActorMapping[redAlertOverlayNames[o]]) { - new LocationInit(new CPos(i, j)), + new LocationInit(cell), new OwnerInit("Neutral") }); } @@ -313,16 +315,13 @@ namespace OpenRA.Utility void UnpackCncTileData(Stream ms) { - for (var i = 0; i < mapSize; i++) - for (var j = 0; j < mapSize; j++) - map.MapTiles.Value[i, j] = new TileReference(); - for (var j = 0; j < mapSize; j++) { for (var i = 0; i < mapSize; i++) { - map.MapTiles.Value[i, j].Type = ms.ReadUInt8(); - map.MapTiles.Value[i, j].Index = ms.ReadUInt8(); + var type = ms.ReadUInt8(); + var index = ms.ReadUInt8(); + map.MapTiles.Value[new CPos(i, j)] = new TerrainTile(type, index); } } } @@ -342,7 +341,7 @@ namespace OpenRA.Utility if (overlayResourceMapping.ContainsKey(kv.Value.ToLower())) res = overlayResourceMapping[kv.Value.ToLower()]; - map.MapResources.Value[cell.X, cell.Y] = new TileReference(res.First, res.Second); + map.MapResources.Value[cell] = new ResourceTile(res.First, res.Second); if (overlayActorMapping.ContainsKey(kv.Value.ToLower())) map.Actors.Value.Add("Actor" + actorCount++,