diff --git a/OpenRA.Game/Graphics/Minimap.cs b/OpenRA.Game/Graphics/Minimap.cs index c766273883..f81c6860b4 100644 --- a/OpenRA.Game/Graphics/Minimap.cs +++ b/OpenRA.Game/Graphics/Minimap.cs @@ -20,11 +20,12 @@ namespace OpenRA.Graphics { public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false) { - var width = map.Bounds.Width; - var height = map.Bounds.Height; + var b = map.Bounds; + var width = b.Width; + var height = b.Height; if (!actualSize) - width = height = Exts.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height)); + width = height = Exts.NextPowerOf2(Math.Max(b.Width, b.Height)); var terrain = new Bitmap(width, height); @@ -37,15 +38,17 @@ namespace OpenRA.Graphics { var c = (int*)bitmapData.Scan0; - for (var x = 0; x < map.Bounds.Width; x++) - for (var y = 0; y < map.Bounds.Height; y++) + for (var x = 0; x < b.Width; x++) + { + for (var y = 0; y < b.Height; y++) { - var mapX = x + map.Bounds.Left; - var mapY = y + map.Bounds.Top; + var mapX = x + b.Left; + var mapY = y + b.Top; var type = tileset.GetTerrainInfo(mapTiles[mapX, mapY]); *(c + (y * bitmapData.Stride >> 2) + x) = type.Color.ToArgb(); } + } } terrain.UnlockBits(bitmapData); @@ -57,6 +60,7 @@ namespace OpenRA.Graphics static Bitmap AddStaticResources(TileSet tileset, Map map, Ruleset resourceRules, Bitmap terrainBitmap) { var terrain = new Bitmap(terrainBitmap); + var b = map.Bounds; var bitmapData = terrain.LockBits(terrain.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); @@ -65,11 +69,12 @@ namespace OpenRA.Graphics { var c = (int*)bitmapData.Scan0; - for (var x = 0; x < map.Bounds.Width; x++) - for (var y = 0; y < map.Bounds.Height; y++) + for (var x = 0; x < b.Width; x++) + { + for (var y = 0; y < b.Height; y++) { - var mapX = x + map.Bounds.Left; - var mapY = y + map.Bounds.Top; + var mapX = x + b.Left; + var mapY = y + b.Top; if (map.MapResources.Value[mapX, mapY].Type == 0) continue; @@ -82,6 +87,7 @@ namespace OpenRA.Graphics *(c + (y * bitmapData.Stride >> 2) + x) = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb(); } + } } terrain.UnlockBits(bitmapData); @@ -92,7 +98,9 @@ namespace OpenRA.Graphics public static Bitmap CustomTerrainBitmap(World world) { var map = world.Map; - var size = Exts.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height)); + var b = map.Bounds; + + var size = Exts.NextPowerOf2(Math.Max(b.Width, b.Height)); var bitmap = new Bitmap(size, size); var bitmapData = bitmap.LockBits(bitmap.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); @@ -101,16 +109,19 @@ namespace OpenRA.Graphics { var c = (int*)bitmapData.Scan0; - for (var x = 0; x < map.Bounds.Width; x++) - for (var y = 0; y < map.Bounds.Height; y++) + for (var x = 0; x < b.Width; x++) + { + for (var y = 0; y < b.Height; y++) { - var mapX = x + map.Bounds.Left; - var mapY = y + map.Bounds.Top; + var mapX = x + b.Left; + var mapY = y + b.Top; var custom = map.CustomTerrain[mapX, mapY]; if (custom == -1) continue; + *(c + (y * bitmapData.Stride >> 2) + x) = world.TileSet[custom].Color.ToArgb(); } + } } bitmap.UnlockBits(bitmapData); @@ -120,7 +131,9 @@ namespace OpenRA.Graphics public static Bitmap ActorsBitmap(World world) { var map = world.Map; - var size = Exts.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height)); + var b = map.Bounds; + + var size = Exts.NextPowerOf2(Math.Max(b.Width, b.Height)); var bitmap = new Bitmap(size, size); var bitmapData = bitmap.LockBits(bitmap.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); @@ -136,8 +149,11 @@ namespace OpenRA.Graphics var color = t.Trait.RadarSignatureColor(t.Actor); foreach (var cell in t.Trait.RadarSignatureCells(t.Actor)) - if (world.Map.Contains(cell)) - *(c + ((cell.Y - world.Map.Bounds.Top) * bitmapData.Stride >> 2) + cell.X - world.Map.Bounds.Left) = color.ToArgb(); + { + var uv = Map.CellToMap(map.TileShape, cell); + if (b.Contains(uv.X, uv.Y)) + *(c + ((uv.Y - b.Top) * bitmapData.Stride >> 2) + uv.X - b.Left) = color.ToArgb(); + } } } @@ -148,7 +164,9 @@ namespace OpenRA.Graphics public static Bitmap ShroudBitmap(World world) { var map = world.Map; - var size = Exts.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height)); + var b = map.Bounds; + + var size = Exts.NextPowerOf2(Math.Max(b.Width, b.Height)); var bitmap = new Bitmap(size, size); if (world.RenderPlayer == null) return bitmap; @@ -158,20 +176,20 @@ namespace OpenRA.Graphics var shroud = Color.Black.ToArgb(); var fog = Color.FromArgb(128, Color.Black).ToArgb(); + var offset = new CVec(b.Left, b.Top); unsafe { var c = (int*)bitmapData.Scan0; - for (var x = 0; x < map.Bounds.Width; x++) - for (var y = 0; y < map.Bounds.Height; y++) - { - var p = new CPos(x + map.Bounds.Left, y + map.Bounds.Top); - if (world.ShroudObscures(p)) - *(c + (y * bitmapData.Stride >> 2) + x) = shroud; - else if (world.FogObscures(p)) - *(c + (y * bitmapData.Stride >> 2) + x) = fog; - } + foreach (var cell in map.Cells) + { + var uv = Map.CellToMap(map.TileShape, cell) - offset; + if (world.ShroudObscures(cell)) + *(c + (uv.Y * bitmapData.Stride >> 2) + uv.X) = shroud; + else if (world.FogObscures(cell)) + *(c + (uv.Y * bitmapData.Stride >> 2) + uv.X) = fog; + } } bitmap.UnlockBits(bitmapData); diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index 11dc2f142d..c194be88e3 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -44,11 +44,12 @@ namespace OpenRA.Graphics { var verticesPerRow = 4*map.Bounds.Width; var cells = viewport.VisibleCells; - var firstRow = cells.TopLeft.Y - map.Bounds.Top; - var lastRow = cells.BottomRight.Y - map.Bounds.Top + 1; + var shape = wr.world.Map.TileShape; - if (lastRow < 0 || firstRow > map.Bounds.Height) - return; + // Only draw the rows that are visible. + // VisibleCells is clamped to the map, so additional checks are unnecessary + var firstRow = Map.CellToMap(shape, cells.TopLeft).Y - map.Bounds.Top; + var lastRow = Map.CellToMap(shape, cells.BottomRight).Y - map.Bounds.Top + 1; Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer( vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow), diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 3c181f6480..83970ec606 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -92,8 +92,8 @@ namespace OpenRA.Graphics var b = map.Bounds; // Expand to corners of cells - var tl = wr.ScreenPxPosition(map.CenterOfCell(new CPos(b.Left, b.Top)) - new WVec(512, 512, 0)); - var br = wr.ScreenPxPosition(map.CenterOfCell(new CPos(b.Right, b.Bottom)) + new WVec(511, 511, 0)); + var tl = wr.ScreenPxPosition(map.CenterOfCell(Map.MapToCell(map.TileShape, new CPos(b.Left, b.Top))) - new WVec(512, 512, 0)); + var br = wr.ScreenPxPosition(map.CenterOfCell(Map.MapToCell(map.TileShape, new CPos(b.Right, b.Bottom))) + new WVec(511, 511, 0)); mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y); CenterLocation = (tl + br) / 2; @@ -138,8 +138,12 @@ namespace OpenRA.Graphics { get { - var ctl = worldRenderer.world.Map.CenterOfCell(VisibleCells.TopLeft) - new WVec(512, 512, 0); - var cbr = worldRenderer.world.Map.CenterOfCell(VisibleCells.BottomRight) + new WVec(511, 511, 0); + // Visible rectangle in world coordinates (expanded to the corners of the cells) + var map = worldRenderer.world.Map; + var ctl = map.CenterOfCell(VisibleCells.TopLeft) - new WVec(512, 512, 0); + var cbr = map.CenterOfCell(VisibleCells.BottomRight) + new WVec(512, 512, 0); + + // Convert to screen coordinates 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); @@ -152,12 +156,16 @@ namespace OpenRA.Graphics { if (cellsDirty) { - // Calculate the intersection of the visible rectangle and the map. + // Visible rectangle in map coordinates var map = worldRenderer.world.Map; - var tl = map.Clamp(map.CellContaining(worldRenderer.Position(TopLeft)) - new CVec(1, 1)); - var br = map.Clamp(map.CellContaining(worldRenderer.Position(BottomRight))); + var ctl = Map.CellToMap(map.TileShape, map.CellContaining(worldRenderer.Position(TopLeft))); + var cbr = Map.CellToMap(map.TileShape, map.CellContaining(worldRenderer.Position(BottomRight))); - cells = new CellRegion(tl, br); + // Add a 2 cell cordon to prevent holes, then convert back to cell coordinates + var tl = map.Clamp(Map.MapToCell(map.TileShape, ctl - new CVec(2, 2))); + var br = map.Clamp(Map.MapToCell(map.TileShape, cbr + new CVec(2, 2))); + + cells = new CellRegion(map.TileShape, tl, br); cellsDirty = false; } diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 194dadca8c..ece91098dc 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -16,6 +16,8 @@ using OpenRA.Primitives; namespace OpenRA { + public enum TileShape { Rectangle, Diamond } + // Describes what is to be loaded in order to run a mod public class Manifest { @@ -33,6 +35,7 @@ namespace OpenRA public readonly Dictionary> Fonts; public readonly Size TileSize = new Size(24, 24); public readonly string NewsUrl; + public readonly TileShape TileShape = TileShape.Rectangle; public Manifest(string mod) { @@ -77,6 +80,9 @@ namespace OpenRA if (yaml.ContainsKey("TileSize")) TileSize = FieldLoader.GetValue("TileSize", yaml["TileSize"].Value); + if (yaml.ContainsKey("TileShape")) + TileShape = FieldLoader.GetValue("TileShape", yaml["TileShape"].Value); + // Allow inherited mods to import parent maps. var compat = new List(); compat.Add(mod); diff --git a/OpenRA.Game/Map/CellLayer.cs b/OpenRA.Game/Map/CellLayer.cs index 7f973b8902..3c0f444840 100644 --- a/OpenRA.Game/Map/CellLayer.cs +++ b/OpenRA.Game/Map/CellLayer.cs @@ -20,22 +20,24 @@ namespace OpenRA public class CellLayer : IEnumerable { public readonly Size Size; + public readonly TileShape Shape; T[] entries; public CellLayer(Map map) - : this(new Size(map.MapSize.X, map.MapSize.Y)) { } + : this(map.TileShape, new Size(map.MapSize.X, map.MapSize.Y)) { } - public CellLayer(Size size) + public CellLayer(TileShape shape, Size size) { Size = size; + Shape = shape; 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; + var uv = Map.CellToMap(Shape, cell); + return uv.Y * Size.Width + uv.X; } /// Gets or sets the using cell coordinates @@ -90,7 +92,7 @@ namespace OpenRA /// 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 result = new CellLayer(layer.Shape, newSize); var width = Math.Min(layer.Size.Width, newSize.Width); var height = Math.Min(layer.Size.Height, newSize.Height); diff --git a/OpenRA.Game/Map/CellRegion.cs b/OpenRA.Game/Map/CellRegion.cs index 3cdccc7fbe..ce7316ecd1 100644 --- a/OpenRA.Game/Map/CellRegion.cs +++ b/OpenRA.Game/Map/CellRegion.cs @@ -23,25 +23,36 @@ namespace OpenRA // Corners of the region public readonly CPos TopLeft; public readonly CPos BottomRight; + readonly TileShape shape; // Corners in map coordinates - // Defined for forward compatibility with diagonal cell grids + // These will only equal TopLeft and BottomRight for TileShape.Rectangular readonly CPos mapTopLeft; readonly CPos mapBottomRight; - public CellRegion(CPos topLeft, CPos bottomRight) + public CellRegion(TileShape shape, CPos topLeft, CPos bottomRight) { + this.shape = shape; TopLeft = topLeft; BottomRight = bottomRight; - mapTopLeft = TopLeft; - mapBottomRight = BottomRight; + mapTopLeft = Map.CellToMap(shape, TopLeft); + mapBottomRight = Map.CellToMap(shape, BottomRight); + } + + /// Expand the specified region with an additional cordon. This may expand the region outside the map borders. + public static CellRegion Expand(CellRegion region, int cordon) + { + var offset = new CVec(cordon, cordon); + var tl = Map.MapToCell(region.shape, Map.CellToMap(region.shape, region.TopLeft) - offset); + var br = Map.MapToCell(region.shape, Map.CellToMap(region.shape, region.BottomRight) + offset); + + return new CellRegion(region.shape, tl, br); } public bool Contains(CPos cell) { - // Defined for forward compatibility with diagonal cell grids - var uv = cell; + var uv = Map.CellToMap(shape, cell); return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y; } @@ -93,7 +104,7 @@ namespace OpenRA v = r.mapTopLeft.Y; } - public CPos Current { get { return new CPos(u, v); } } + public CPos Current { get { return Map.MapToCell(r.shape, 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 8cbe791316..484362f138 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -76,6 +76,8 @@ namespace OpenRA public bool AllowStartUnitConfig = true; public Bitmap CustomPreview; + public readonly TileShape TileShape; + [FieldLoader.LoadUsing("LoadOptions")] public MapOptions Options; @@ -125,11 +127,12 @@ namespace OpenRA public static Map FromTileset(TileSet tileset) { var size = new Size(1, 1); + var tileShape = Game.modData.Manifest.TileShape; var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0); var makeMapTiles = Exts.Lazy(() => { - var ret = new CellLayer(size); + var ret = new CellLayer(tileShape, size); ret.Clear(tileRef); return ret; }); @@ -142,7 +145,7 @@ namespace OpenRA MapSize = new int2(size), Tileset = tileset.Id, Options = new MapOptions(), - MapResources = Exts.Lazy(() => new CellLayer(size)), + MapResources = Exts.Lazy(() => new CellLayer(tileShape, size)), MapTiles = makeMapTiles, Actors = Exts.Lazy(() => new Dictionary()), Smudges = Exts.Lazy(() => new List()) @@ -242,6 +245,7 @@ namespace OpenRA MapTiles = Exts.Lazy(() => LoadMapTiles()); MapResources = Exts.Lazy(() => LoadResourceTiles()); + TileShape = Game.modData.Manifest.TileShape; // The Uid is calculated from the data on-disk, so // format changes must be flushed to disk. @@ -262,9 +266,9 @@ namespace OpenRA rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this)); cachedTileSet = Exts.Lazy(() => Rules.TileSets[Tileset]); - var tl = new CPos(Bounds.Left, Bounds.Top); - var br = new CPos(Bounds.Right - 1, Bounds.Bottom - 1); - Cells = new CellRegion(tl, br); + var tl = Map.MapToCell(TileShape, new CPos(Bounds.Left, Bounds.Top)); + var br = Map.MapToCell(TileShape, new CPos(Bounds.Right - 1, Bounds.Bottom - 1)); + Cells = new CellRegion(TileShape, tl, br); CustomTerrain = new CellLayer(this); foreach (var cell in Cells) @@ -465,17 +469,79 @@ namespace OpenRA public bool Contains(CPos cell) { - return Bounds.Contains(cell.X, cell.Y); + var uv = CellToMap(TileShape, cell); + return Bounds.Contains(uv.X, uv.Y); } - public WPos CenterOfCell(CPos c) + public WPos CenterOfCell(CPos cell) { - return new WPos(1024 * c.X + 512, 1024 * c.Y + 512, 0); + if (TileShape == TileShape.Rectangle) + return new WPos(1024 * cell.X + 512, 1024 * cell.Y + 512, 0); + + // Convert from diamond cell position (x, y) to world position (u, v): + // (a) Consider the relationships: + // - Center of origin cell is (512, 512) + // - +x adds (512, 512) to world pos + // - +y adds (-512, 512) to world pos + // (b) Therefore: + // - ax + by adds (a - b) * 512 + 512 to u + // - ax + by adds (a + b) * 512 + 512 to v + return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), 0); } public CPos CellContaining(WPos pos) { - return new CPos(pos.X / 1024, pos.Y / 1024); + if (TileShape == TileShape.Rectangle) + return new CPos(pos.X / 1024, pos.Y / 1024); + + // Convert from world position to diamond cell position: + // (a) Subtract (512, 512) to move the rotation center to the middle of the corner cell + // (b) Rotate axes by -pi/4 + // (c) Add 512 to x (but not y) to realign the cell + // (d) Divide by 1024 to find final cell coords + var u = (pos.Y + pos.X - 512) / 1024; + var v = (pos.Y - pos.X) / 1024; + return new CPos(u, v); + } + + public static CPos MapToCell(TileShape shape, CPos map) + { + if (shape == TileShape.Rectangle) + return map; + + // Convert from rectangular map position to diamond cell position + // - The staggered rows make this fiddly (hint: draw a diagram!) + // (a) Consider the relationships: + // - +1u (even -> odd) adds (1, -1) to (x, y) + // - +1v (even -> odd) adds (1, 0) to (x, y) + // - +1v (odd -> even) adds (0, 1) to (x, y) + // (b) Therefore: + // - au + 2bv adds (a + b) to (x, y) + // - a correction factor is added if v is odd + var offset = (map.Y & 1) == 1 ? 1 : 0; + var y = (map.Y - offset) / 2 - map.X; + var x = map.Y - y; + return new CPos(x, y); + } + + public static CPos CellToMap(TileShape shape, CPos cell) + { + if (shape == TileShape.Rectangle) + return cell; + + // Convert from diamond cell (x, y) position to rectangular map position (u, v) + // - The staggered rows make this fiddly (hint: draw a diagram!) + // (a) Consider the relationships: + // - +1x (even -> odd) adds (0, 1) to (u, v) + // - +1x (odd -> even) adds (1, 1) to (u, v) + // - +1y (even -> odd) adds (-1, 1) to (u, v) + // - +1y (odd -> even) adds (0, 1) to (u, v) + // (b) Therefore: + // - ax + by adds (a - b)/2 to u (only even increments count) + // - ax + by adds a + b to v + var u = (cell.X - cell.Y) / 2; + var v = cell.X + cell.Y; + return new CPos(u, v); } public int FacingBetween(CPos cell, CPos towards, int fallbackfacing) @@ -497,7 +563,10 @@ namespace OpenRA 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)); + + var tl = Map.MapToCell(TileShape, new CPos(Bounds.Left, Bounds.Top)); + var br = Map.MapToCell(TileShape, new CPos(Bounds.Right - 1, Bounds.Bottom - 1)); + Cells = new CellRegion(TileShape, tl, br); } string ComputeHash() @@ -597,17 +666,18 @@ namespace OpenRA return cachedTileSet.Value[GetTerrainIndex(cell)]; } - public CPos Clamp(CPos xy) + public CPos Clamp(CPos cell) { - var r = Bounds; - return xy.Clamp(new Rectangle(r.X, r.Y, r.Width - 1, r.Height - 1)); + var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1); + return MapToCell(TileShape, CellToMap(TileShape, cell).Clamp(bounds)); } public CPos ChooseRandomCell(MersenneTwister rand) { - return new CPos( - rand.Next(Bounds.Left, Bounds.Right), - rand.Next(Bounds.Top, Bounds.Bottom)); + var x = rand.Next(Bounds.Left, Bounds.Right); + var y = rand.Next(Bounds.Top, Bounds.Bottom); + + return MapToCell(TileShape, new CPos(x, y)); } public CPos ChooseRandomEdgeCell(MersenneTwister rand) @@ -615,15 +685,16 @@ namespace OpenRA var isX = rand.Next(2) == 0; var edge = rand.Next(2) == 0; - return new CPos( - isX ? rand.Next(Bounds.Left, Bounds.Right) : (edge ? Bounds.Left : Bounds.Right), - !isX ? rand.Next(Bounds.Top, Bounds.Bottom) : (edge ? Bounds.Top : Bounds.Bottom)); + var x = isX ? rand.Next(Bounds.Left, Bounds.Right) : (edge ? Bounds.Left : Bounds.Right); + var y = !isX ? rand.Next(Bounds.Top, Bounds.Bottom) : (edge ? Bounds.Top : Bounds.Bottom); + + return MapToCell(TileShape, new CPos(x, y)); } public WRange DistanceToEdge(WPos pos, WVec dir) { - var tl = CenterOfCell(new CPos(Bounds.Left, Bounds.Top)) - new WVec(512, 512, 0); - var br = CenterOfCell(new CPos(Bounds.Right, Bounds.Bottom)) + new WVec(511, 511, 0); + var tl = CenterOfCell(Cells.TopLeft) - new WVec(512, 512, 0); + var br = CenterOfCell(Cells.BottomRight) + new WVec(511, 511, 0); var x = dir.X == 0 ? int.MaxValue : ((dir.X < 0 ? tl.X : br.X) - pos.X) / dir.X; var y = dir.Y == 0 ? int.MaxValue : ((dir.Y < 0 ? tl.Y : br.Y) - pos.Y) / dir.Y; return new WRange(Math.Min(x, y) * dir.Length); diff --git a/OpenRA.Game/Widgets/MapPreviewWidget.cs b/OpenRA.Game/Widgets/MapPreviewWidget.cs index 384bd8b316..c70aab3c0f 100644 --- a/OpenRA.Game/Widgets/MapPreviewWidget.cs +++ b/OpenRA.Game/Widgets/MapPreviewWidget.cs @@ -129,9 +129,10 @@ namespace OpenRA.Widgets tooltipContainer.Value.RemoveTooltip(); } - public int2 ConvertToPreview(CPos point) + public int2 ConvertToPreview(CPos cell) { var preview = Preview(); + var point = Map.CellToMap(preview.Map.TileShape, cell); var dx = (int)(previewScale * (point.X - preview.Bounds.Left)); var dy = (int)(previewScale * (point.Y - preview.Bounds.Top)); return new int2(mapRect.X + dx, mapRect.Y + dy); diff --git a/OpenRA.Mods.RA/Move/PathSearch.cs b/OpenRA.Mods.RA/Move/PathSearch.cs index 1b61a86814..93a3ccb2fb 100755 --- a/OpenRA.Mods.RA/Move/PathSearch.cs +++ b/OpenRA.Mods.RA/Move/PathSearch.cs @@ -287,7 +287,7 @@ namespace OpenRA.Mods.RA.Move while (CellInfoPool.Count > 0) { var cellInfo = GetFromPool(); - if (cellInfo.Size != mapSize) + if (cellInfo.Size != mapSize || cellInfo.Shape != self.World.Map.TileShape) { Log.Write("debug", "Discarding old pooled CellInfo of wrong size."); continue; diff --git a/OpenRA.Mods.RA/ShroudRenderer.cs b/OpenRA.Mods.RA/ShroudRenderer.cs index 302d466984..10a44b1fc5 100644 --- a/OpenRA.Mods.RA/ShroudRenderer.cs +++ b/OpenRA.Mods.RA/ShroudRenderer.cs @@ -177,19 +177,19 @@ namespace OpenRA.Mods.RA return useExtendedIndex ? u ^ uside : u & 0x0F; } - static int ObserverShroudedEdges(CPos p, Rectangle bounds, bool useExtendedIndex) + static int ObserverShroudedEdges(Map map, CPos p, bool useExtendedIndex) { var u = 0; - if (p.Y == bounds.Top) u |= 0x13; - if (p.X == bounds.Right - 1) u |= 0x26; - if (p.Y == bounds.Bottom - 1) u |= 0x4C; - if (p.X == bounds.Left) u |= 0x89; + if (!map.Contains(p + new CVec(0, -1))) u |= 0x13; + if (!map.Contains(p + new CVec(1, 0))) u |= 0x26; + if (!map.Contains(p + new CVec(0, 1))) u |= 0x4C; + if (!map.Contains(p + new CVec(-1, 0))) u |= 0x89; var uside = u & 0x0F; - if (p.X == bounds.Left && p.Y == bounds.Top) u |= 0x01; - if (p.X == bounds.Right - 1 && p.Y == bounds.Top) u |= 0x02; - if (p.X == bounds.Right - 1 && p.Y == bounds.Bottom - 1) u |= 0x04; - if (p.X == bounds.Left && p.Y == bounds.Bottom - 1) u |= 0x08; + if (!map.Contains(p + new CVec(-1, -1))) u |= 0x01; + if (!map.Contains(p + new CVec(1, -1))) u |= 0x02; + if (!map.Contains(p + new CVec(1, 1))) u |= 0x04; + if (!map.Contains(p + new CVec(-1, 1))) u |= 0x08; return useExtendedIndex ? u ^ uside : u & 0x0F; } @@ -197,11 +197,19 @@ namespace OpenRA.Mods.RA public void WorldLoaded(World w, WorldRenderer wr) { // Initialize tile cache - foreach (var cell in map.Cells) + // Adds a 1-cell border around the border to cover any sprites peeking outside the map + foreach (var cell in CellRegion.Expand(w.Map.Cells, 1)) { var screen = wr.ScreenPosition(w.Map.CenterOfCell(cell)); var variant = Game.CosmeticRandom.Next(info.ShroudVariants.Length); tiles[cell] = new ShroudTile(cell, screen, variant); + + // Set the cells outside the border so they don't need to be touched again + if (!map.Contains(cell)) + { + var index = info.UseExtendedIndex ? 240 : 15; + tiles[cell].Shroud = shroudSprites[variant * variantStride + spriteMap[index]]; + } } fogPalette = wr.Palette(info.FogPalette); @@ -229,7 +237,7 @@ namespace OpenRA.Mods.RA foreach (var cell in map.Cells) { var t = tiles[cell]; - var shrouded = ObserverShroudedEdges(t.Position, map.Bounds, info.UseExtendedIndex); + var shrouded = ObserverShroudedEdges(map, t.Position, info.UseExtendedIndex); t.Shroud = shrouded != 0 ? shroudSprites[t.Variant * variantStride + spriteMap[shrouded]] : null; t.Fog = shrouded != 0 ? fogSprites[t.Variant * variantStride + spriteMap[shrouded]] : null; @@ -253,7 +261,7 @@ namespace OpenRA.Mods.RA { Update(shroud); - foreach (var cell in wr.Viewport.VisibleCells) + foreach (var cell in CellRegion.Expand(wr.Viewport.VisibleCells, 1)) { var t = tiles[cell]; diff --git a/OpenRA.Mods.RA/Widgets/RadarWidget.cs b/OpenRA.Mods.RA/Widgets/RadarWidget.cs index 960e484b5e..e7b4fdaa96 100755 --- a/OpenRA.Mods.RA/Widgets/RadarWidget.cs +++ b/OpenRA.Mods.RA/Widgets/RadarWidget.cs @@ -115,7 +115,7 @@ namespace OpenRA.Mods.RA.Widgets var cell = MinimapPixelToCell(mi.Location); var pos = world.Map.CenterOfCell(cell); if ((mi.Event == MouseInputEvent.Down || mi.Event == MouseInputEvent.Move) && mi.Button == MouseButton.Left) - worldRenderer.Viewport.Center(world.Map.CenterOfCell(cell)); + worldRenderer.Viewport.Center(pos); if (mi.Event == MouseInputEvent.Down && mi.Button == MouseButton.Right) { @@ -242,8 +242,8 @@ namespace OpenRA.Mods.RA.Widgets int2 CellToMinimapPixel(CPos p) { - var mapOrigin = new CPos(world.Map.Bounds.Left, world.Map.Bounds.Top); - var mapOffset = p - mapOrigin; + var mapOrigin = new CVec(world.Map.Bounds.Left, world.Map.Bounds.Top); + var mapOffset = Map.CellToMap(world.Map.TileShape, p) - mapOrigin; return new int2(mapRect.X, mapRect.Y) + (previewScale * new float2(mapOffset.X, mapOffset.Y)).ToInt2(); } @@ -253,7 +253,7 @@ namespace OpenRA.Mods.RA.Widgets var viewOrigin = new float2(mapRect.X, mapRect.Y); var mapOrigin = new float2(world.Map.Bounds.Left, world.Map.Bounds.Top); var fcell = mapOrigin + (1f / previewScale) * (p - viewOrigin); - return new CPos((int)fcell.X, (int)fcell.Y); + return Map.MapToCell(world.Map.TileShape, new CPos((int)fcell.X, (int)fcell.Y)); } } } diff --git a/OpenRA.Utility/LegacyMapImporter.cs b/OpenRA.Utility/LegacyMapImporter.cs index ded0b9d2ab..ce83937535 100644 --- a/OpenRA.Utility/LegacyMapImporter.cs +++ b/OpenRA.Utility/LegacyMapImporter.cs @@ -149,8 +149,8 @@ namespace OpenRA.Utility map.Smudges = Exts.Lazy(() => new List()); map.Actors = Exts.Lazy(() => new Dictionary()); - map.MapResources = Exts.Lazy(() => new CellLayer(size)); - map.MapTiles = Exts.Lazy(() => new CellLayer(size)); + map.MapResources = Exts.Lazy(() => new CellLayer(TileShape.Rectangle, size)); + map.MapTiles = Exts.Lazy(() => new CellLayer(TileShape.Rectangle, size)); map.Options = new MapOptions(); diff --git a/mods/ts/maps/blank-conquest.oramap b/mods/ts/maps/blank-conquest.oramap deleted file mode 100644 index 512fdbb8fb..0000000000 Binary files a/mods/ts/maps/blank-conquest.oramap and /dev/null differ diff --git a/mods/ts/maps/blank-testmap/map.bin b/mods/ts/maps/blank-conquest/map.bin similarity index 96% rename from mods/ts/maps/blank-testmap/map.bin rename to mods/ts/maps/blank-conquest/map.bin index 011a9c1426..1538bf2097 100644 Binary files a/mods/ts/maps/blank-testmap/map.bin and b/mods/ts/maps/blank-conquest/map.bin differ diff --git a/mods/ts/maps/blank-testmap/map.yaml b/mods/ts/maps/blank-conquest/map.yaml similarity index 52% rename from mods/ts/maps/blank-testmap/map.yaml rename to mods/ts/maps/blank-conquest/map.yaml index 3cfb223bb1..1f575b9014 100644 --- a/mods/ts/maps/blank-testmap/map.yaml +++ b/mods/ts/maps/blank-conquest/map.yaml @@ -4,17 +4,17 @@ MapFormat: 6 RequiresMod: ts -Title: Blank Test Map +Title: Blank -Description: Test Map +Description: Placeholder -Author: Reaperrr +Author: Matthias Mailänder Tileset: INTERIOR MapSize: 128,128 -Bounds: 16,16,96,96 +Bounds: 32,16,48,96 UseAsShellmap: False @@ -56,53 +56,17 @@ Players: Actors: Actor0: mpspawn - Location: 23,23 + Location: 50,-24 Owner: Neutral Actor1: mpspawn - Location: 103,23 + Location: 126,-24 + Owner: Neutral + Actor2: mpspawn + Location: 88,14 + Owner: Neutral + Actor3: mpspawn + Location: 88,-62 Owner: Neutral - Actor2: gatech - Location: 90,80 - Owner: Creeps - Actor3: natech - Location: 86,80 - Owner: Creeps - Actor4: naradr - Location: 82,80 - Owner: Creeps - Actor5: hvr - Location: 40,80 - Owner: Creeps - Actor6: cyborg - Location: 38,80 - Owner: Creeps - Actor7: cyc2 - Location: 36,80 - Owner: Creeps - Actor8: bike - Location: 34,80 - Owner: Creeps - Actor9: ttnk - Location: 32,80 - Owner: Creeps - Actor10: mmch - Location: 30,80 - Owner: Creeps - Actor11: gapowr - Location: 90,85 - Owner: Creeps - Actor12: gapowr - Location: 87,85 - Owner: Creeps - Actor13: napowr - Location: 84,85 - Owner: Creeps - Actor14: napowr - Location: 81,85 - Owner: Creeps - Actor15: napowr - Location: 78,85 - Owner: Creeps Smudges: diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index b9a38c0848..6b9e1eb993 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -142,6 +142,7 @@ TileSets: mods/ts/tilesets/interior.yaml TileSize: 48,24 +TileShape: Diamond Music: mods/ts/music.yaml diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml index 45cf0b5c68..5cf2c51d14 100644 --- a/mods/ts/sequences/misc.yaml +++ b/mods/ts/sequences/misc.yaml @@ -1,24 +1,24 @@ overlay: - build-valid-interior: cellsel #TODO remove backfall to RA tileset - Start: 3 - build-valid-snow: cellsel - Start: 3 - build-valid-temperat: cellsel - Start: 3 - build-invalid: cellsel - Start: 6 - target-select: cellsel - Start: 10 - target-valid-desert: cellsel - Start: 3 - target-valid-interior: cellsel - Start: 3 - target-valid-snow: cellsel - Start: 3 - target-valid-temperat: cellsel - Start: 3 - target-invalid: cellsel - Start: 6 + build-valid-interior: place + Start: 0 + build-valid-snow: place + Start: 0 + build-valid-temperat: place + Start: 0 + build-invalid: place + Start: 1 + target-select: place + Start: 1 + target-valid-desert: place + Start: 1 + target-valid-interior: place + Start: 1 + target-valid-snow: place + Start: 1 + target-valid-temperat: place + Start: 1 + target-invalid: place + Start: 1 poweroff: offline: poweroff diff --git a/mods/ts/tilesets/interior.yaml b/mods/ts/tilesets/interior.yaml index a6ec53d092..f98b79d3ee 100644 --- a/mods/ts/tilesets/interior.yaml +++ b/mods/ts/tilesets/interior.yaml @@ -9,7 +9,7 @@ Terrain: TerrainType@Clear: Type: Clear AcceptsSmudgeType: Crater, Scorch - Color: 0, 0, 0 + Color: 116, 85, 55 TargetTypes: Ground TerrainType@Tiberium: Type: Tiberium