diff --git a/OpenRA.Game/CPos.cs b/OpenRA.Game/CPos.cs index 2940bb8b67..94a610a52b 100644 --- a/OpenRA.Game/CPos.cs +++ b/OpenRA.Game/CPos.cs @@ -44,7 +44,7 @@ namespace OpenRA public MPos ToMPos(Map map) { - return ToMPos(map.TileShape); + return ToMPos(map.Grid.Type); } public MPos ToMPos(TileShape shape) diff --git a/OpenRA.Game/Graphics/Minimap.cs b/OpenRA.Game/Graphics/Minimap.cs index 90b8069f60..9a881fe104 100644 --- a/OpenRA.Game/Graphics/Minimap.cs +++ b/OpenRA.Game/Graphics/Minimap.cs @@ -20,12 +20,12 @@ namespace OpenRA.Graphics { public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false) { - var isDiamond = map.TileShape == TileShape.Diamond; + var isDiamond = map.Grid.Type == TileShape.Diamond; var b = map.Bounds; // Fudge the heightmap offset by adding as much extra as we need / can. // This tries to correct for our incorrect assumption that MPos == PPos - var heightOffset = Math.Min(map.MaximumTerrainHeight, map.MapSize.Y - b.Bottom); + var heightOffset = Math.Min(map.Grid.MaximumTerrainHeight, map.MapSize.Y - b.Bottom); var width = b.Width; var height = b.Height + heightOffset; @@ -81,12 +81,12 @@ namespace OpenRA.Graphics static Bitmap AddStaticResources(TileSet tileset, Map map, Ruleset resourceRules, Bitmap terrainBitmap) { var terrain = new Bitmap(terrainBitmap); - var isDiamond = map.TileShape == TileShape.Diamond; + var isDiamond = map.Grid.Type == TileShape.Diamond; var b = map.Bounds; // Fudge the heightmap offset by adding as much extra as we need / can // This tries to correct for our incorrect assumption that MPos == PPos - var heightOffset = Math.Min(map.MaximumTerrainHeight, map.MapSize.Y - b.Bottom); + var heightOffset = Math.Min(map.Grid.MaximumTerrainHeight, map.MapSize.Y - b.Bottom); var width = b.Width; var height = b.Height + heightOffset; diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index 08cecb0660..81c07151de 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -71,7 +71,7 @@ namespace OpenRA.Graphics { var pos = sprite == null ? float2.Zero : worldRenderer.ScreenPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size; - Update(cell.ToMPos(map.TileShape), sprite, pos); + Update(cell.ToMPos(map.Grid.Type), sprite, pos); } public void Update(MPos uv, Sprite sprite, float2 pos) diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index d8a69b94a1..df9332a557 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -92,15 +92,15 @@ namespace OpenRA.Graphics public Viewport(WorldRenderer wr, Map map) { worldRenderer = wr; + var grid = Game.ModData.Manifest.Get(); // Calculate map bounds in world-px if (wr.World.Type == WorldType.Editor) { // The full map is visible in the editor - var ts = Game.ModData.Manifest.TileSize; - var width = map.MapSize.X * ts.Width; - var height = map.MapSize.Y * ts.Height; - if (wr.World.Map.TileShape == TileShape.Diamond) + var width = map.MapSize.X * grid.TileSize.Width; + var height = map.MapSize.Y * grid.TileSize.Height; + if (wr.World.Map.Grid.Type == TileShape.Diamond) height /= 2; mapBounds = new Rectangle(0, 0, width, height); @@ -115,23 +115,22 @@ namespace OpenRA.Graphics } Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1; - tileSize = Game.ModData.Manifest.TileSize; + tileSize = grid.TileSize; } public CPos ViewToWorld(int2 view) { var world = worldRenderer.Viewport.ViewToWorldPx(view); var map = worldRenderer.World.Map; - var ts = Game.ModData.Manifest.TileSize; - var candidates = CandidateMouseoverCells(world); + var candidates = CandidateMouseoverCells(world).ToList(); var tileSet = worldRenderer.World.TileSet; foreach (var uv in candidates) { // Coarse filter to nearby cells - var p = map.CenterOfCell(uv.ToCPos(map.TileShape)); + var p = map.CenterOfCell(uv.ToCPos(map.Grid.Type)); var s = worldRenderer.ScreenPxPosition(p); - if (Math.Abs(s.X - world.X) <= ts.Width && Math.Abs(s.Y - world.Y) <= ts.Height) + if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height) { var ramp = 0; if (map.Contains(uv)) @@ -153,11 +152,11 @@ namespace OpenRA.Graphics // Mouse is not directly over a cell (perhaps on a cliff) // Try and find the closest cell - if (candidates.Any()) + if (candidates.Count > 0) { return candidates.OrderBy(uv => { - var p = map.CenterOfCell(uv.ToCPos(map.TileShape)); + var p = map.CenterOfCell(uv.ToCPos(map.Grid.Type)); var s = worldRenderer.ScreenPxPosition(p); var dx = Math.Abs(s.X - world.X); var dy = Math.Abs(s.Y - world.Y); @@ -177,8 +176,8 @@ namespace OpenRA.Graphics var minPos = worldRenderer.ProjectedPosition(world); // Find all the cells that could potentially have been clicked - var a = map.CellContaining(minPos - new WVec(1024, 0, 0)).ToMPos(map.TileShape); - var b = map.CellContaining(minPos + new WVec(512, 512 * map.MaximumTerrainHeight, 0)).ToMPos(map.TileShape); + var a = map.CellContaining(minPos - new WVec(1024, 0, 0)).ToMPos(map.Grid.Type); + var b = map.CellContaining(minPos + new WVec(512, 512 * map.Grid.MaximumTerrainHeight, 0)).ToMPos(map.Grid.Type); for (var v = b.V; v >= a.V; v--) for (var u = b.U; u >= a.U; u--) @@ -244,7 +243,7 @@ namespace OpenRA.Graphics // Diamond tile shapes don't have straight edges, and so we need // an additional cell margin to include the cells that are half // visible on each edge. - if (map.TileShape == TileShape.Diamond) + if (map.Grid.Type == TileShape.Diamond) { tl = new PPos(tl.U - 1, tl.V - 1); br = new PPos(br.U + 1, br.V + 1); diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 69d0c718b1..71baf6350f 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -25,6 +25,7 @@ namespace OpenRA.Graphics static readonly int[][] RangeCircleStartRotations = Exts.MakeArray(RangeCircleSegments, i => WRot.FromFacing(8 * i).AsMatrix()); static readonly int[][] RangeCircleEndRotations = Exts.MakeArray(RangeCircleSegments, i => WRot.FromFacing(8 * i + 6).AsMatrix()); + public readonly Size TileSize; public readonly World World; public readonly Theater Theater; public Viewport Viewport { get; private set; } @@ -40,6 +41,7 @@ namespace OpenRA.Graphics internal WorldRenderer(World world) { World = world; + TileSize = World.Map.Grid.TileSize; Viewport = new Viewport(this, world.Map); createPaletteReference = CreatePaletteReference; @@ -231,8 +233,7 @@ namespace OpenRA.Graphics // Conversion between world and screen coordinates public float2 ScreenPosition(WPos pos) { - var ts = Game.ModData.Manifest.TileSize; - return new float2(ts.Width * pos.X / 1024f, ts.Height * (pos.Y - pos.Z) / 1024f); + return new float2(TileSize.Width * pos.X / 1024f, TileSize.Height * (pos.Y - pos.Z) / 1024f); } public int2 ScreenPxPosition(WPos pos) @@ -245,10 +246,9 @@ namespace OpenRA.Graphics // For scaling vectors to pixel sizes in the voxel renderer public void ScreenVectorComponents(WVec vec, out float x, out float y, out float z) { - var ts = Game.ModData.Manifest.TileSize; - x = ts.Width * vec.X / 1024f; - y = ts.Height * vec.Y / 1024f; - z = ts.Height * vec.Z / 1024f; + x = TileSize.Width * vec.X / 1024f; + y = TileSize.Height * vec.Y / 1024f; + z = TileSize.Height * vec.Z / 1024f; } // For scaling vectors to pixel sizes in the voxel renderer @@ -269,8 +269,7 @@ namespace OpenRA.Graphics public float ScreenZPosition(WPos pos, int offset) { - var ts = Game.ModData.Manifest.TileSize; - return ZPosition(pos, offset) * ts.Height / 1024f; + return ZPosition(pos, offset) * TileSize.Height / 1024f; } static int ZPosition(WPos pos, int offset) @@ -284,8 +283,7 @@ namespace OpenRA.Graphics /// public WPos ProjectedPosition(int2 screenPx) { - var ts = Game.ModData.Manifest.TileSize; - return new WPos(1024 * screenPx.X / ts.Width, 1024 * screenPx.Y / ts.Height, 0); + return new WPos(1024 * screenPx.X / TileSize.Width, 1024 * screenPx.Y / TileSize.Height, 0); } public void Dispose() diff --git a/OpenRA.Game/MPos.cs b/OpenRA.Game/MPos.cs index 32f97077a4..ba97cba0dc 100644 --- a/OpenRA.Game/MPos.cs +++ b/OpenRA.Game/MPos.cs @@ -38,7 +38,7 @@ namespace OpenRA public CPos ToCPos(Map map) { - return ToCPos(map.TileShape); + return ToCPos(map.Grid.Type); } public CPos ToCPos(TileShape shape) diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 918691613f..cba7be926e 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -10,14 +10,12 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.IO; using System.Linq; using OpenRA.Primitives; namespace OpenRA { - public enum TileShape { Rectangle, Diamond } public interface IGlobalModData { } public sealed class SpriteSequenceFormat : IGlobalModData @@ -47,31 +45,13 @@ namespace OpenRA public readonly MiniYaml LobbyDefaults; public readonly Dictionary> Fonts; - public readonly Size TileSize = new Size(24, 24); - public readonly TileShape TileShape = TileShape.Rectangle; - public readonly byte MaximumTerrainHeight = 0; public readonly string[] SpriteFormats = { }; - [Desc("(x,y,z) offset of the full cell and each sub-cell", "X & Y should be between -512 ... 512 and Z >= 0")] - public readonly WVec[] SubCellOffsets = - { - new WVec(0, 0, 0), // full cell - index 0 - new WVec(-299, -256, 0), // top left - index 1 - new WVec(256, -256, 0), // top right - index 2 - new WVec(0, 0, 0), // center - index 3 - new WVec(-299, 256, 0), // bottom left - index 4 - new WVec(256, 256, 0), // bottom right - index 5 - }; - - [Desc("Default subcell index used if SubCellInit is absent", "0 - full cell, 1 - first sub-cell")] - public readonly int SubCellDefaultIndex = 3; - readonly string[] reservedModuleNames = { "Metadata", "Folders", "MapFolders", "Packages", "Rules", "Sequences", "VoxelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons", "Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", - "ServerTraits", "LoadScreen", "LobbyDefaults", "Fonts", "TileSize", "MaximumTerrainHeight", - "TileShape", "SubCells", "SupportsMapsFrom", "SpriteFormats" }; + "ServerTraits", "LoadScreen", "LobbyDefaults", "Fonts", "SupportsMapsFrom", "SpriteFormats" }; readonly TypeDictionary modules = new TypeDictionary(); readonly Dictionary yaml; @@ -118,33 +98,6 @@ namespace OpenRA return Pair.New(nd["Font"].Value, Exts.ParseIntegerInvariant(nd["Size"].Value)); }); - if (yaml.ContainsKey("TileSize")) - TileSize = FieldLoader.GetValue("TileSize", yaml["TileSize"].Value); - - if (yaml.ContainsKey("TileShape")) - TileShape = FieldLoader.GetValue("TileShape", yaml["TileShape"].Value); - - if (yaml.ContainsKey("MaximumTerrainHeight")) - MaximumTerrainHeight = FieldLoader.GetValue("MaximumTerrainHeight", yaml["MaximumTerrainHeight"].Value); - - if (yaml.ContainsKey("SubCells")) - { - var subcells = yaml["SubCells"].ToDictionary(); - - // Read (x,y,z) offset (relative to cell center) pairs for positioning subcells - if (subcells.ContainsKey("Offsets")) - SubCellOffsets = FieldLoader.GetValue("Offsets", subcells["Offsets"].Value); - - if (subcells.ContainsKey("DefaultIndex")) - SubCellDefaultIndex = FieldLoader.GetValue("DefaultIndex", subcells["DefaultIndex"].Value); - else // Otherwise set the default subcell index to the middle subcell entry - SubCellDefaultIndex = SubCellOffsets.Length / 2; - } - - // Validate default index - 0 for no subcells, otherwise > 1 & <= subcell count (offset triples count - 1) - if (SubCellDefaultIndex < (SubCellOffsets.Length > 1 ? 1 : 0) || SubCellDefaultIndex >= SubCellOffsets.Length) - throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells"); - // 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 fe20b6966d..e497259208 100644 --- a/OpenRA.Game/Map/CellLayer.cs +++ b/OpenRA.Game/Map/CellLayer.cs @@ -26,7 +26,7 @@ namespace OpenRA readonly T[] entries; public CellLayer(Map map) - : this(map.TileShape, new Size(map.MapSize.X, map.MapSize.Y)) { } + : this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { } public CellLayer(TileShape shape, Size size) { diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 3f3f45a160..4ecf69e976 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -151,8 +151,7 @@ namespace OpenRA }; public const int MaxTilesInCircleRange = 50; - public readonly TileShape TileShape; - public readonly byte MaximumTerrainHeight; + public readonly MapGrid Grid; [FieldLoader.Ignore] public readonly WVec[] SubCellOffsets; public readonly SubCell DefaultSubCell; @@ -275,7 +274,7 @@ namespace OpenRA public Map(TileSet tileset, int width, int height) { var size = new Size(width, height); - var tileShape = Game.ModData.Manifest.TileShape; + Grid = Game.ModData.Manifest.Get(); var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0); Title = "Name your map here"; @@ -287,29 +286,27 @@ namespace OpenRA Videos = new MapVideos(); Options = new MapOptions(); - MapResources = Exts.Lazy(() => new CellLayer(tileShape, size)); + MapResources = Exts.Lazy(() => new CellLayer(Grid.Type, size)); MapTiles = Exts.Lazy(() => { - var ret = new CellLayer(tileShape, size); + var ret = new CellLayer(Grid.Type, size); ret.Clear(tileRef); - if (MaximumTerrainHeight > 0) + if (Grid.MaximumTerrainHeight > 0) ret.CellEntryChanged += UpdateProjection; return ret; }); MapHeight = Exts.Lazy(() => { - var ret = new CellLayer(tileShape, size); + var ret = new CellLayer(Grid.Type, size); ret.Clear(0); - if (MaximumTerrainHeight > 0) + if (Grid.MaximumTerrainHeight > 0) ret.CellEntryChanged += UpdateProjection; return ret; }); SpawnPoints = Exts.Lazy(() => new CPos[0]); - TileShape = tileShape; - MaximumTerrainHeight = Game.ModData.Manifest.MaximumTerrainHeight; PostInit(); } @@ -374,12 +371,11 @@ namespace OpenRA MapResources = Exts.Lazy(LoadResourceTiles); MapHeight = Exts.Lazy(LoadMapHeight); - TileShape = Game.ModData.Manifest.TileShape; - MaximumTerrainHeight = Game.ModData.Manifest.MaximumTerrainHeight; + Grid = Game.ModData.Manifest.Get(); - SubCellOffsets = Game.ModData.Manifest.SubCellOffsets; + SubCellOffsets = Grid.SubCellOffsets; LastSubCell = (SubCell)(SubCellOffsets.Length - 1); - DefaultSubCell = (SubCell)Game.ModData.Manifest.SubCellDefaultIndex; + DefaultSubCell = (SubCell)Grid.SubCellDefaultIndex; if (Container.Exists("map.png")) using (var dataStream = Container.GetContent("map.png")) @@ -417,7 +413,7 @@ namespace OpenRA var tl = new MPos(0, 0).ToCPos(this); var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this); - AllCells = new CellRegion(TileShape, tl, br); + AllCells = new CellRegion(Grid.Type, tl, br); var btl = new PPos(Bounds.Left, Bounds.Top); var bbr = new PPos(Bounds.Right - 1, Bounds.Bottom - 1); @@ -427,10 +423,10 @@ namespace OpenRA foreach (var uv in AllCells.MapCoords) CustomTerrain[uv] = byte.MaxValue; - var leftDelta = TileShape == TileShape.Diamond ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0); - var topDelta = TileShape == TileShape.Diamond ? new WVec(0, -512, 0) : new WVec(512, -512, 0); - var rightDelta = TileShape == TileShape.Diamond ? new WVec(512, 0, 0) : new WVec(512, 512, 0); - var bottomDelta = TileShape == TileShape.Diamond ? new WVec(0, 512, 0) : new WVec(-512, 512, 0); + var leftDelta = Grid.Type == TileShape.Diamond ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0); + var topDelta = Grid.Type == TileShape.Diamond ? new WVec(0, -512, 0) : new WVec(512, -512, 0); + var rightDelta = Grid.Type == TileShape.Diamond ? new WVec(512, 0, 0) : new WVec(512, 512, 0); + var bottomDelta = Grid.Type == TileShape.Diamond ? new WVec(0, 512, 0) : new WVec(-512, 512, 0); CellCorners = CellCornerHalfHeights.Select(ramp => new WVec[] { leftDelta + new WVec(0, 0, 512 * ramp[0]), @@ -453,7 +449,7 @@ namespace OpenRA // Initialize collections foreach (var cell in AllCells) { - var uv = cell.ToMPos(TileShape); + var uv = cell.ToMPos(Grid.Type); cellProjection[uv] = new PPos[0]; inverseCellProjection[uv] = new List(); } @@ -467,9 +463,9 @@ namespace OpenRA { MPos uv; - if (MaximumTerrainHeight == 0) + if (Grid.MaximumTerrainHeight == 0) { - uv = cell.ToMPos(TileShape); + uv = cell.ToMPos(Grid.Type); cellProjection[cell] = new[] { (PPos)uv }; var inverse = inverseCellProjection[uv]; inverse.Clear(); @@ -480,7 +476,7 @@ namespace OpenRA if (!initializedCellProjection) InitializeCellProjection(); - uv = cell.ToMPos(TileShape); + uv = cell.ToMPos(Grid.Type); // Remove old reverse projection foreach (var puv in cellProjection[uv]) @@ -635,7 +631,7 @@ namespace OpenRA } } - if (MaximumTerrainHeight > 0) + if (Grid.MaximumTerrainHeight > 0) tiles.CellEntryChanged += UpdateProjection; return tiles; @@ -652,11 +648,11 @@ namespace OpenRA s.Position = header.HeightsOffset; for (var i = 0; i < MapSize.X; i++) for (var j = 0; j < MapSize.Y; j++) - tiles[new MPos(i, j)] = s.ReadUInt8().Clamp((byte)0, MaximumTerrainHeight); + tiles[new MPos(i, j)] = s.ReadUInt8().Clamp((byte)0, Grid.MaximumTerrainHeight); } } - if (MaximumTerrainHeight > 0) + if (Grid.MaximumTerrainHeight > 0) tiles.CellEntryChanged += UpdateProjection; return tiles; @@ -701,8 +697,8 @@ namespace OpenRA // Data offsets var tilesOffset = 17; - var heightsOffset = MaximumTerrainHeight > 0 ? 3 * MapSize.X * MapSize.Y + 17 : 0; - var resourcesOffset = (MaximumTerrainHeight > 0 ? 4 : 3) * MapSize.X * MapSize.Y + 17; + var heightsOffset = Grid.MaximumTerrainHeight > 0 ? 3 * MapSize.X * MapSize.Y + 17 : 0; + var resourcesOffset = (Grid.MaximumTerrainHeight > 0 ? 4 : 3) * MapSize.X * MapSize.Y + 17; writer.Write((uint)tilesOffset); writer.Write((uint)heightsOffset); @@ -751,7 +747,7 @@ namespace OpenRA // .ToMPos() returns the same result if the X and Y coordinates // are switched. X < Y is invalid in the Diamond coordinate system, // so we pre-filter these to avoid returning the wrong result - if (TileShape == TileShape.Diamond && cell.X < cell.Y) + if (Grid.Type == TileShape.Diamond && cell.X < cell.Y) return false; return Contains(cell.ToMPos(this)); @@ -767,7 +763,7 @@ namespace OpenRA bool ContainsAllProjectedCellsCovering(MPos uv) { - if (MaximumTerrainHeight == 0) + if (Grid.MaximumTerrainHeight == 0) return Contains((PPos)uv); foreach (var puv in ProjectedCellsCovering(uv)) @@ -783,7 +779,7 @@ namespace OpenRA public WPos CenterOfCell(CPos cell) { - if (TileShape == TileShape.Rectangle) + if (Grid.Type == 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): @@ -815,7 +811,7 @@ namespace OpenRA public CPos CellContaining(WPos pos) { - if (TileShape == TileShape.Rectangle) + if (Grid.Type == TileShape.Rectangle) return new CPos(pos.X / 1024, pos.Y / 1024); // Convert from world position to diamond cell position: @@ -834,7 +830,7 @@ namespace OpenRA public PPos ProjectedCellCovering(WPos pos) { var projectedPos = pos - new WVec(0, pos.Z, pos.Z); - return (PPos)CellContaining(projectedPos).ToMPos(TileShape); + return (PPos)CellContaining(projectedPos).ToMPos(Grid.Type); } static readonly PPos[] NoProjectedCells = { }; @@ -881,7 +877,7 @@ namespace OpenRA var tl = new MPos(0, 0).ToCPos(this); var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this); - AllCells = new CellRegion(TileShape, tl, br); + AllCells = new CellRegion(Grid.Type, tl, br); } public void SetBounds(PPos tl, PPos br) @@ -896,7 +892,7 @@ namespace OpenRA // for diamond cells. var wtop = tl.V * 1024; var wbottom = (br.V + 1) * 1024; - if (TileShape == TileShape.Diamond) + if (Grid.Type == TileShape.Diamond) { wtop /= 2; wbottom /= 2; @@ -995,7 +991,7 @@ namespace OpenRA public MPos Clamp(MPos uv) { - if (MaximumTerrainHeight == 0) + if (Grid.MaximumTerrainHeight == 0) return (MPos)Clamp((PPos)uv); // Already in bounds, so don't need to do anything. @@ -1030,7 +1026,7 @@ namespace OpenRA if (!unProjected.Any()) { // Adjust V until we find a cell that works - for (var x = 2; x <= 2 * MaximumTerrainHeight; x++) + for (var x = 2; x <= 2 * Grid.MaximumTerrainHeight; x++) { var dv = ((x & 1) == 1 ? 1 : -1) * x / 2; var test = new PPos(projected.U, projected.V + dv); @@ -1070,12 +1066,12 @@ namespace OpenRA cells = Unproject(new PPos(u, v)); } while (!cells.Any()); - return cells.Random(rand).ToCPos(TileShape); + return cells.Random(rand).ToCPos(Grid.Type); } public CPos ChooseClosestEdgeCell(CPos cell) { - return ChooseClosestEdgeCell(cell.ToMPos(TileShape)).ToCPos(TileShape); + return ChooseClosestEdgeCell(cell.ToMPos(Grid.Type)).ToCPos(Grid.Type); } public MPos ChooseClosestEdgeCell(MPos uv) @@ -1101,7 +1097,7 @@ namespace OpenRA if (!unProjected.Any()) { // Adjust V until we find a cell that works - for (var x = 2; x <= 2 * MaximumTerrainHeight; x++) + for (var x = 2; x <= 2 * Grid.MaximumTerrainHeight; x++) { var dv = ((x & 1) == 1 ? 1 : -1) * x / 2; var test = new PPos(edge.U, edge.V + dv); @@ -1137,7 +1133,7 @@ namespace OpenRA cells = Unproject(new PPos(u, v)); } while (!cells.Any()); - return cells.Random(rand).ToCPos(TileShape); + return cells.Random(rand).ToCPos(Grid.Type); } public WDist DistanceToEdge(WPos pos, WVec dir) diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs new file mode 100644 index 0000000000..fcad1641a9 --- /dev/null +++ b/OpenRA.Game/Map/MapGrid.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System.Drawing; +using System.IO; + +namespace OpenRA +{ + public enum TileShape { Rectangle, Diamond } + + public class MapGrid : IGlobalModData + { + public readonly TileShape Type = TileShape.Rectangle; + public readonly Size TileSize = new Size(24, 24); + public readonly byte MaximumTerrainHeight = 0; + public readonly byte SubCellDefaultIndex = byte.MaxValue; + public readonly WVec[] SubCellOffsets = + { + new WVec(0, 0, 0), // full cell - index 0 + new WVec(-299, -256, 0), // top left - index 1 + new WVec(256, -256, 0), // top right - index 2 + new WVec(0, 0, 0), // center - index 3 + new WVec(-299, 256, 0), // bottom left - index 4 + new WVec(256, 256, 0), // bottom right - index 5 + }; + + public MapGrid(MiniYaml yaml) + { + FieldLoader.Load(this, yaml); + + // The default subcell index defaults to the middle entry + if (SubCellDefaultIndex == byte.MaxValue) + SubCellDefaultIndex = (byte)(SubCellOffsets.Length / 2); + else if (SubCellDefaultIndex < (SubCellOffsets.Length > 1 ? 1 : 0) || SubCellDefaultIndex >= SubCellOffsets.Length) + throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells"); + } + } +} diff --git a/OpenRA.Game/Map/ProjectedCellRegion.cs b/OpenRA.Game/Map/ProjectedCellRegion.cs index 1e7b719142..d8a21cc467 100644 --- a/OpenRA.Game/Map/ProjectedCellRegion.cs +++ b/OpenRA.Game/Map/ProjectedCellRegion.cs @@ -42,7 +42,8 @@ namespace OpenRA // been projected into this region if they have height > 0. // Each height step is equivalent to 512 WRange units, which is one MPos // step for diamond cells, but only half a MPos step for classic cells. Doh! - var heightOffset = map.TileShape == TileShape.Diamond ? map.MaximumTerrainHeight : map.MaximumTerrainHeight / 2; + var maxHeight = map.Grid.MaximumTerrainHeight; + var heightOffset = map.Grid.Type == TileShape.Diamond ? maxHeight : maxHeight / 2; // Use the MapHeight data array to clamp the bottom coordinate so it doesn't overflow the map mapBottomRight = map.MapHeight.Value.Clamp(new MPos(bottomRight.U, bottomRight.V + heightOffset)); diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 30dbdd1844..fd235f027a 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -94,6 +94,7 @@ + diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs index a4415e723e..75901479e5 100644 --- a/OpenRA.Game/Traits/World/ScreenMap.cs +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -36,9 +36,9 @@ namespace OpenRA.Traits public ScreenMap(World world, ScreenMapInfo info) { - var ts = Game.ModData.Manifest.TileSize; - var width = world.Map.MapSize.X * ts.Width; - var height = world.Map.MapSize.Y * ts.Height; + var size = world.Map.Grid.TileSize; + var width = world.Map.MapSize.X * size.Width; + var height = world.Map.MapSize.Y * size.Height; partitionedFrozenActors = new Cache>( _ => new SpatiallyPartitioned(width, height, info.BinSize)); partitionedActors = new SpatiallyPartitioned(width, height, info.BinSize); diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs index 24dea6fcde..c5f288c571 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs @@ -48,7 +48,8 @@ namespace OpenRA.Mods.Common.Widgets preview.IsVisible = () => editorWidget.CurrentBrush == this; preview.Template = world.TileSet.Templates.First(t => t.Value.Id == template).Value; - bounds = worldRenderer.Theater.TemplateBounds(preview.Template, Game.ModData.Manifest.TileSize, world.Map.TileShape); + var grid = world.Map.Grid; + bounds = worldRenderer.Theater.TemplateBounds(preview.Template, grid.TileSize, grid.Type); // The preview widget may be rendered by the higher-level code before it is ticked. // Force a manual tick to ensure the bounds are set correctly for this first draw. @@ -106,7 +107,7 @@ namespace OpenRA.Mods.Common.Widgets continue; mapTiles[c] = new TerrainTile(Template, index); - mapHeight[c] = (byte)(baseHeight + template[index].Height).Clamp(0, map.MaximumTerrainHeight); + mapHeight[c] = (byte)(baseHeight + template[index].Height).Clamp(0, map.Grid.MaximumTerrainHeight); } } } diff --git a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs index 48b2bd31e9..145beac8cb 100644 --- a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs @@ -106,8 +106,7 @@ namespace OpenRA.Mods.Common.Graphics { var groundPos = voxel.pos - new WVec(0, 0, wr.World.Map.DistanceAboveTerrain(voxel.pos).Length); - var ts = Game.ModData.Manifest.TileSize; - var groundZ = ts.Height * (groundPos.Z - voxel.pos.Z) / 1024f; + var groundZ = wr.World.Map.Grid.TileSize.Height * (groundPos.Z - voxel.pos.Z) / 1024f; var pxOrigin = wr.ScreenPosition(voxel.pos); var shadowOrigin = pxOrigin - groundZ * (new float2(renderProxy.ShadowDirection, 1)); diff --git a/OpenRA.Mods.Common/Pathfinder/CellInfoLayerPool.cs b/OpenRA.Mods.Common/Pathfinder/CellInfoLayerPool.cs index 1bfb39a6c0..305769b8e5 100644 --- a/OpenRA.Mods.Common/Pathfinder/CellInfoLayerPool.cs +++ b/OpenRA.Mods.Common/Pathfinder/CellInfoLayerPool.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Pathfinder CellLayer.CreateInstance( mpos => new CellInfo(int.MaxValue, int.MaxValue, mpos.ToCPos(map), CellStatus.Unvisited), new Size(map.MapSize.X, map.MapSize.Y), - map.TileShape); + map.Grid.Type); } public PooledCellInfoLayer Get() diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs index 1ba44586c7..ff64e6a07f 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs @@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var pr in Players.Players.Values) wr.UpdatePalettesForPlayer(pr.Name, pr.Color, false); - var ts = Game.ModData.Manifest.TileSize; + var ts = world.Map.Grid.TileSize; var width = world.Map.MapSize.X * ts.Width; var height = world.Map.MapSize.Y * ts.Height; screenMap = new SpatiallyPartitioned(width, height, info.BinSize); diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 82c413d521..0eb364fba4 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.UtilityCommands internal static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv) { var value = Exts.ParseIntegerInvariant(input); - var ts = Game.ModData.Manifest.TileSize; + var ts = Game.ModData.Manifest.Get().TileSize; var world = value * 1024 * scaleMult / (scaleDiv * ts.Height); var cells = world / 1024; var subcells = world - 1024 * cells; @@ -69,7 +69,7 @@ namespace OpenRA.Mods.Common.UtilityCommands internal static void ConvertInt2ToWVec(ref string input) { var offset = FieldLoader.GetValue("(value)", input); - var ts = Game.ModData.Manifest.TileSize; + var ts = Game.ModData.Manifest.Get().TileSize; var world = new WVec(offset.X * 1024 / ts.Width, offset.Y * 1024 / ts.Height, 0); input = world.ToString(); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs index 5f9b351568..66abc4b518 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs @@ -67,12 +67,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic var frame = sequence.Frames != null ? sequence.Frames.Last() : resource.MaxDensity - 1; layerPreview.GetSprite = () => sequence.GetSprite(frame); - var tileWidth = Game.ModData.Manifest.TileSize.Width; - var tileHeight = Game.ModData.Manifest.TileSize.Height; - layerPreview.Bounds.Width = tileWidth; - layerPreview.Bounds.Height = tileHeight; - newResourcePreviewTemplate.Bounds.Width = tileWidth + (layerPreview.Bounds.X * 2); - newResourcePreviewTemplate.Bounds.Height = tileHeight + (layerPreview.Bounds.Y * 2); + var tileSize = Game.ModData.Manifest.Get().TileSize; + layerPreview.Bounds.Width = tileSize.Width; + layerPreview.Bounds.Height = tileSize.Height; + newResourcePreviewTemplate.Bounds.Width = tileSize.Width + (layerPreview.Bounds.X * 2); + newResourcePreviewTemplate.Bounds.Height = tileSize.Height + (layerPreview.Bounds.Y * 2); newResourcePreviewTemplate.IsVisible = () => true; newResourcePreviewTemplate.GetTooltipText = () => resource.Name; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs index 185d39c274..6d261e3150 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs @@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic width = Math.Max(2, width); height = Math.Max(2, height); - var maxTerrainHeight = Game.ModData.Manifest.MaximumTerrainHeight; + var maxTerrainHeight = world.Map.Grid.MaximumTerrainHeight; var tileset = modRules.TileSets[tilesetDropDown.Text]; var map = new Map(tileset, width + 2, height + maxTerrainHeight + 2); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs index 6693e6e3a3..b288485e98 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs @@ -68,7 +68,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic var preview = item.Get("TILE_PREVIEW"); var template = tileset.Templates[tileId]; - var bounds = worldRenderer.Theater.TemplateBounds(template, Game.ModData.Manifest.TileSize, worldRenderer.World.Map.TileShape); + var grid = worldRenderer.World.Map.Grid; + var bounds = worldRenderer.Theater.TemplateBounds(template, grid.TileSize, grid.Type); // Scale templates to fit within the panel var scale = 1f; diff --git a/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs b/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs index bf83048d7e..8f6ca309cb 100644 --- a/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs +++ b/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs @@ -62,6 +62,7 @@ namespace OpenRA.Mods.Common.Widgets readonly Color spawnColor, spawnContrastColor; readonly int2 spawnLabelOffset; readonly int cellWidth; + readonly TileShape shape; public Func Preview = () => null; public Func> SpawnOccupants = () => new Dictionary(); @@ -83,7 +84,8 @@ namespace OpenRA.Mods.Common.Widgets spawnContrastColor = ChromeMetrics.Get("SpawnContrastColor"); spawnLabelOffset = ChromeMetrics.Get("SpawnLabelOffset"); - cellWidth = Game.ModData.Manifest.TileShape == TileShape.Diamond ? 2 : 1; + shape = Game.ModData.Manifest.Get().Type; + cellWidth = shape == TileShape.Diamond ? 2 : 1; } protected MapPreviewWidget(MapPreviewWidget other) @@ -106,6 +108,7 @@ namespace OpenRA.Mods.Common.Widgets spawnContrastColor = ChromeMetrics.Get("SpawnContrastColor"); spawnLabelOffset = ChromeMetrics.Get("SpawnLabelOffset"); + shape = other.shape; cellWidth = other.cellWidth; } @@ -138,8 +141,7 @@ namespace OpenRA.Mods.Common.Widgets public int2 ConvertToPreview(CPos cell) { var preview = Preview(); - var tileShape = Game.ModData.Manifest.TileShape; - var point = cell.ToMPos(tileShape); + var point = cell.ToMPos(shape); var dx = (int)(previewScale * cellWidth * (point.U - preview.Bounds.Left)); var dy = (int)(previewScale * (point.V - preview.Bounds.Top)); diff --git a/OpenRA.Mods.Common/Widgets/RadarWidget.cs b/OpenRA.Mods.Common/Widgets/RadarWidget.cs index 47d13d66ce..deefde107f 100644 --- a/OpenRA.Mods.Common/Widgets/RadarWidget.cs +++ b/OpenRA.Mods.Common/Widgets/RadarWidget.cs @@ -64,7 +64,7 @@ namespace OpenRA.Mods.Common.Widgets this.worldRenderer = worldRenderer; radarPings = world.WorldActor.TraitOrDefault(); - isDiamond = world.Map.TileShape == TileShape.Diamond; + isDiamond = world.Map.Grid.Type == TileShape.Diamond; cellWidth = isDiamond ? 2 : 1; previewWidth = world.Map.MapSize.X; previewHeight = world.Map.MapSize.Y; @@ -379,7 +379,7 @@ namespace OpenRA.Mods.Common.Widgets if (!world.Map.Contains(cell.First)) continue; - var uv = cell.First.ToMPos(world.Map.TileShape); + var uv = cell.First.ToMPos(world.Map.Grid.Type); var color = cell.Second.ToArgb(); if (isDiamond) { diff --git a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs index d46470999f..bc91d7e7e4 100644 --- a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs +++ b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs @@ -41,8 +41,9 @@ namespace OpenRA.Mods.Common.Widgets if (template == null) return; - var ts = Game.ModData.Manifest.TileSize; - var shape = Game.ModData.Manifest.TileShape; + var grid = Game.ModData.Manifest.Get(); + var ts = grid.TileSize; + var shape = grid.Type; bounds = worldRenderer.Theater.TemplateBounds(template, ts, shape); } } @@ -70,8 +71,9 @@ namespace OpenRA.Mods.Common.Widgets if (template == null) return; - var ts = Game.ModData.Manifest.TileSize; - var shape = Game.ModData.Manifest.TileShape; + var grid = Game.ModData.Manifest.Get(); + var ts = grid.TileSize; + var shape = grid.Type; var scale = GetScale(); var sb = new Rectangle((int)(scale * bounds.X), (int)(scale * bounds.Y), (int)(scale * bounds.Width), (int)(scale * bounds.Height)); diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index cfb39841f1..f029dd1d5e 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -201,6 +201,10 @@ Fonts: Missions: ./mods/cnc/missions.yaml +MapGrid: + TileSize: 24,24 + Type: Rectangle + SupportsMapsFrom: cnc SpriteFormats: ShpTD, TmpTD, ShpTS, TmpRA diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 39ba0f6ee1..98f1770ed2 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -49,7 +49,9 @@ Sequences: TileSets: ./mods/d2k/tilesets/arrakis.yaml -TileSize: 32,32 +MapGrid: + TileSize: 32,32 + Type: Rectangle Cursors: ./mods/d2k/cursors.yaml diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index d3b8970280..50140769e9 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -201,6 +201,10 @@ Fonts: Missions: ./mods/ra/missions.yaml +MapGrid: + TileSize: 24,24 + Type: Rectangle + SupportsMapsFrom: ra SpriteFormats: ShpTD, TmpRA, TmpTD, ShpTS diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 413592aa8a..9ff47d5397 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -111,12 +111,12 @@ TileSets: ./mods/ts/tilesets/temperate.yaml ./mods/ts/tilesets/snow.yaml -TileSize: 48,24 -TileShape: Diamond -SubCells: - Offsets: 0,0,0, -256,128,0, 0,-128,0, 256,128,0 - DefaultIndex: 2 -MaximumTerrainHeight: 16 +MapGrid: + TileSize: 48,24 + Type: Diamond + MaximumTerrainHeight: 16 + SubCellOffsets: 0,0,0, -256,128,0, 0,-128,0, 256,128,0 + SubCellDefaultIndex: 2 Cursors: ./mods/ts/cursors.yaml