Merge pull request #5778 from pchote/isometric

Add support for diamond cell grids
This commit is contained in:
Matthias Mailänder
2014-07-01 16:59:22 +02:00
18 changed files with 254 additions and 163 deletions

View File

@@ -20,11 +20,12 @@ namespace OpenRA.Graphics
{ {
public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false) public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false)
{ {
var width = map.Bounds.Width; var b = map.Bounds;
var height = map.Bounds.Height; var width = b.Width;
var height = b.Height;
if (!actualSize) 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); var terrain = new Bitmap(width, height);
@@ -37,16 +38,18 @@ namespace OpenRA.Graphics
{ {
var c = (int*)bitmapData.Scan0; var c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.Bounds.Width; x++) for (var x = 0; x < b.Width; x++)
for (var y = 0; y < map.Bounds.Height; y++)
{ {
var mapX = x + map.Bounds.Left; for (var y = 0; y < b.Height; y++)
var mapY = y + map.Bounds.Top; {
var mapX = x + b.Left;
var mapY = y + b.Top;
var type = tileset.GetTerrainInfo(mapTiles[mapX, mapY]); var type = tileset.GetTerrainInfo(mapTiles[mapX, mapY]);
*(c + (y * bitmapData.Stride >> 2) + x) = type.Color.ToArgb(); *(c + (y * bitmapData.Stride >> 2) + x) = type.Color.ToArgb();
} }
} }
}
terrain.UnlockBits(bitmapData); terrain.UnlockBits(bitmapData);
return terrain; return terrain;
@@ -57,6 +60,7 @@ namespace OpenRA.Graphics
static Bitmap AddStaticResources(TileSet tileset, Map map, Ruleset resourceRules, Bitmap terrainBitmap) static Bitmap AddStaticResources(TileSet tileset, Map map, Ruleset resourceRules, Bitmap terrainBitmap)
{ {
var terrain = new Bitmap(terrainBitmap); var terrain = new Bitmap(terrainBitmap);
var b = map.Bounds;
var bitmapData = terrain.LockBits(terrain.Bounds(), var bitmapData = terrain.LockBits(terrain.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
@@ -65,11 +69,12 @@ namespace OpenRA.Graphics
{ {
var c = (int*)bitmapData.Scan0; var c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.Bounds.Width; x++) for (var x = 0; x < b.Width; x++)
for (var y = 0; y < map.Bounds.Height; y++)
{ {
var mapX = x + map.Bounds.Left; for (var y = 0; y < b.Height; y++)
var mapY = y + map.Bounds.Top; {
var mapX = x + b.Left;
var mapY = y + b.Top;
if (map.MapResources.Value[mapX, mapY].Type == 0) if (map.MapResources.Value[mapX, mapY].Type == 0)
continue; continue;
@@ -83,6 +88,7 @@ namespace OpenRA.Graphics
*(c + (y * bitmapData.Stride >> 2) + x) = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb(); *(c + (y * bitmapData.Stride >> 2) + x) = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb();
} }
} }
}
terrain.UnlockBits(bitmapData); terrain.UnlockBits(bitmapData);
@@ -92,7 +98,9 @@ namespace OpenRA.Graphics
public static Bitmap CustomTerrainBitmap(World world) public static Bitmap CustomTerrainBitmap(World world)
{ {
var map = world.Map; 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 bitmap = new Bitmap(size, size);
var bitmapData = bitmap.LockBits(bitmap.Bounds(), var bitmapData = bitmap.LockBits(bitmap.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
@@ -101,17 +109,20 @@ namespace OpenRA.Graphics
{ {
var c = (int*)bitmapData.Scan0; var c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.Bounds.Width; x++) for (var x = 0; x < b.Width; x++)
for (var y = 0; y < map.Bounds.Height; y++)
{ {
var mapX = x + map.Bounds.Left; for (var y = 0; y < b.Height; y++)
var mapY = y + map.Bounds.Top; {
var mapX = x + b.Left;
var mapY = y + b.Top;
var custom = map.CustomTerrain[mapX, mapY]; var custom = map.CustomTerrain[mapX, mapY];
if (custom == -1) if (custom == -1)
continue; continue;
*(c + (y * bitmapData.Stride >> 2) + x) = world.TileSet[custom].Color.ToArgb(); *(c + (y * bitmapData.Stride >> 2) + x) = world.TileSet[custom].Color.ToArgb();
} }
} }
}
bitmap.UnlockBits(bitmapData); bitmap.UnlockBits(bitmapData);
return bitmap; return bitmap;
@@ -120,7 +131,9 @@ namespace OpenRA.Graphics
public static Bitmap ActorsBitmap(World world) public static Bitmap ActorsBitmap(World world)
{ {
var map = world.Map; 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 bitmap = new Bitmap(size, size);
var bitmapData = bitmap.LockBits(bitmap.Bounds(), var bitmapData = bitmap.LockBits(bitmap.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
@@ -136,8 +149,11 @@ namespace OpenRA.Graphics
var color = t.Trait.RadarSignatureColor(t.Actor); var color = t.Trait.RadarSignatureColor(t.Actor);
foreach (var cell in t.Trait.RadarSignatureCells(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) public static Bitmap ShroudBitmap(World world)
{ {
var map = world.Map; 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 bitmap = new Bitmap(size, size);
if (world.RenderPlayer == null) if (world.RenderPlayer == null)
return bitmap; return bitmap;
@@ -158,19 +176,19 @@ namespace OpenRA.Graphics
var shroud = Color.Black.ToArgb(); var shroud = Color.Black.ToArgb();
var fog = Color.FromArgb(128, Color.Black).ToArgb(); var fog = Color.FromArgb(128, Color.Black).ToArgb();
var offset = new CVec(b.Left, b.Top);
unsafe unsafe
{ {
var c = (int*)bitmapData.Scan0; var c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.Bounds.Width; x++) foreach (var cell in map.Cells)
for (var y = 0; y < map.Bounds.Height; y++)
{ {
var p = new CPos(x + map.Bounds.Left, y + map.Bounds.Top); var uv = Map.CellToMap(map.TileShape, cell) - offset;
if (world.ShroudObscures(p)) if (world.ShroudObscures(cell))
*(c + (y * bitmapData.Stride >> 2) + x) = shroud; *(c + (uv.Y * bitmapData.Stride >> 2) + uv.X) = shroud;
else if (world.FogObscures(p)) else if (world.FogObscures(cell))
*(c + (y * bitmapData.Stride >> 2) + x) = fog; *(c + (uv.Y * bitmapData.Stride >> 2) + uv.X) = fog;
} }
} }

View File

@@ -44,11 +44,12 @@ namespace OpenRA.Graphics
{ {
var verticesPerRow = 4*map.Bounds.Width; var verticesPerRow = 4*map.Bounds.Width;
var cells = viewport.VisibleCells; var cells = viewport.VisibleCells;
var firstRow = cells.TopLeft.Y - map.Bounds.Top; var shape = wr.world.Map.TileShape;
var lastRow = cells.BottomRight.Y - map.Bounds.Top + 1;
if (lastRow < 0 || firstRow > map.Bounds.Height) // Only draw the rows that are visible.
return; // 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( Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow), vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),

View File

@@ -92,8 +92,8 @@ namespace OpenRA.Graphics
var b = map.Bounds; var b = map.Bounds;
// Expand to corners of cells // Expand to corners of cells
var tl = wr.ScreenPxPosition(map.CenterOfCell(new CPos(b.Left, b.Top)) - new WVec(512, 512, 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(new CPos(b.Right, b.Bottom)) + new WVec(511, 511, 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); mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
CenterLocation = (tl + br) / 2; CenterLocation = (tl + br) / 2;
@@ -138,8 +138,12 @@ namespace OpenRA.Graphics
{ {
get get
{ {
var ctl = worldRenderer.world.Map.CenterOfCell(VisibleCells.TopLeft) - new WVec(512, 512, 0); // Visible rectangle in world coordinates (expanded to the corners of the cells)
var cbr = worldRenderer.world.Map.CenterOfCell(VisibleCells.BottomRight) + new WVec(511, 511, 0); 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 tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl)).Clamp(ScreenClip);
var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr)).Clamp(ScreenClip); var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr)).Clamp(ScreenClip);
return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y); return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
@@ -152,12 +156,16 @@ namespace OpenRA.Graphics
{ {
if (cellsDirty) if (cellsDirty)
{ {
// Calculate the intersection of the visible rectangle and the map. // Visible rectangle in map coordinates
var map = worldRenderer.world.Map; var map = worldRenderer.world.Map;
var tl = map.Clamp(map.CellContaining(worldRenderer.Position(TopLeft)) - new CVec(1, 1)); var ctl = Map.CellToMap(map.TileShape, map.CellContaining(worldRenderer.Position(TopLeft)));
var br = map.Clamp(map.CellContaining(worldRenderer.Position(BottomRight))); 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; cellsDirty = false;
} }

View File

@@ -16,6 +16,8 @@ using OpenRA.Primitives;
namespace OpenRA namespace OpenRA
{ {
public enum TileShape { Rectangle, Diamond }
// Describes what is to be loaded in order to run a mod // Describes what is to be loaded in order to run a mod
public class Manifest public class Manifest
{ {
@@ -33,6 +35,7 @@ namespace OpenRA
public readonly Dictionary<string, Pair<string, int>> Fonts; public readonly Dictionary<string, Pair<string, int>> Fonts;
public readonly Size TileSize = new Size(24, 24); public readonly Size TileSize = new Size(24, 24);
public readonly string NewsUrl; public readonly string NewsUrl;
public readonly TileShape TileShape = TileShape.Rectangle;
public Manifest(string mod) public Manifest(string mod)
{ {
@@ -77,6 +80,9 @@ namespace OpenRA
if (yaml.ContainsKey("TileSize")) if (yaml.ContainsKey("TileSize"))
TileSize = FieldLoader.GetValue<Size>("TileSize", yaml["TileSize"].Value); TileSize = FieldLoader.GetValue<Size>("TileSize", yaml["TileSize"].Value);
if (yaml.ContainsKey("TileShape"))
TileShape = FieldLoader.GetValue<TileShape>("TileShape", yaml["TileShape"].Value);
// Allow inherited mods to import parent maps. // Allow inherited mods to import parent maps.
var compat = new List<string>(); var compat = new List<string>();
compat.Add(mod); compat.Add(mod);

View File

@@ -20,22 +20,24 @@ namespace OpenRA
public class CellLayer<T> : IEnumerable<T> public class CellLayer<T> : IEnumerable<T>
{ {
public readonly Size Size; public readonly Size Size;
public readonly TileShape Shape;
T[] entries; T[] entries;
public CellLayer(Map map) 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; Size = size;
Shape = shape;
entries = new T[size.Width * size.Height]; entries = new T[size.Width * size.Height];
} }
// Resolve an array index from cell coordinates // Resolve an array index from cell coordinates
int Index(CPos cell) int Index(CPos cell)
{ {
// This will eventually define a distinct case for diagonal cell grids var uv = Map.CellToMap(Shape, cell);
return cell.Y * Size.Width + cell.X; return uv.Y * Size.Width + uv.X;
} }
/// <summary>Gets or sets the <see cref="OpenRA.CellLayer"/> using cell coordinates</summary> /// <summary>Gets or sets the <see cref="OpenRA.CellLayer"/> using cell coordinates</summary>
@@ -90,7 +92,7 @@ namespace OpenRA
/// <summary>Create a new layer by resizing another layer. New cells are filled with defaultValue.</summary> /// <summary>Create a new layer by resizing another layer. New cells are filled with defaultValue.</summary>
public static CellLayer<T> Resize<T>(CellLayer<T> layer, Size newSize, T defaultValue) public static CellLayer<T> Resize<T>(CellLayer<T> layer, Size newSize, T defaultValue)
{ {
var result = new CellLayer<T>(newSize); var result = new CellLayer<T>(layer.Shape, newSize);
var width = Math.Min(layer.Size.Width, newSize.Width); var width = Math.Min(layer.Size.Width, newSize.Width);
var height = Math.Min(layer.Size.Height, newSize.Height); var height = Math.Min(layer.Size.Height, newSize.Height);

View File

@@ -23,25 +23,36 @@ namespace OpenRA
// Corners of the region // Corners of the region
public readonly CPos TopLeft; public readonly CPos TopLeft;
public readonly CPos BottomRight; public readonly CPos BottomRight;
readonly TileShape shape;
// Corners in map coordinates // 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 mapTopLeft;
readonly CPos mapBottomRight; readonly CPos mapBottomRight;
public CellRegion(CPos topLeft, CPos bottomRight) public CellRegion(TileShape shape, CPos topLeft, CPos bottomRight)
{ {
this.shape = shape;
TopLeft = topLeft; TopLeft = topLeft;
BottomRight = bottomRight; BottomRight = bottomRight;
mapTopLeft = TopLeft; mapTopLeft = Map.CellToMap(shape, TopLeft);
mapBottomRight = BottomRight; mapBottomRight = Map.CellToMap(shape, BottomRight);
}
/// <summary>Expand the specified region with an additional cordon. This may expand the region outside the map borders.</summary>
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) public bool Contains(CPos cell)
{ {
// Defined for forward compatibility with diagonal cell grids var uv = Map.CellToMap(shape, cell);
var uv = cell;
return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y; 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; 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; } } object IEnumerator.Current { get { return Current; } }
public void Dispose() { } public void Dispose() { }
} }

View File

@@ -76,6 +76,8 @@ namespace OpenRA
public bool AllowStartUnitConfig = true; public bool AllowStartUnitConfig = true;
public Bitmap CustomPreview; public Bitmap CustomPreview;
public readonly TileShape TileShape;
[FieldLoader.LoadUsing("LoadOptions")] [FieldLoader.LoadUsing("LoadOptions")]
public MapOptions Options; public MapOptions Options;
@@ -125,11 +127,12 @@ namespace OpenRA
public static Map FromTileset(TileSet tileset) public static Map FromTileset(TileSet tileset)
{ {
var size = new Size(1, 1); var size = new Size(1, 1);
var tileShape = Game.modData.Manifest.TileShape;
var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0); var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
var makeMapTiles = Exts.Lazy(() => var makeMapTiles = Exts.Lazy(() =>
{ {
var ret = new CellLayer<TerrainTile>(size); var ret = new CellLayer<TerrainTile>(tileShape, size);
ret.Clear(tileRef); ret.Clear(tileRef);
return ret; return ret;
}); });
@@ -142,7 +145,7 @@ namespace OpenRA
MapSize = new int2(size), MapSize = new int2(size),
Tileset = tileset.Id, Tileset = tileset.Id,
Options = new MapOptions(), Options = new MapOptions(),
MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(size)), MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(tileShape, size)),
MapTiles = makeMapTiles, MapTiles = makeMapTiles,
Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()), Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
Smudges = Exts.Lazy(() => new List<SmudgeReference>()) Smudges = Exts.Lazy(() => new List<SmudgeReference>())
@@ -242,6 +245,7 @@ namespace OpenRA
MapTiles = Exts.Lazy(() => LoadMapTiles()); MapTiles = Exts.Lazy(() => LoadMapTiles());
MapResources = Exts.Lazy(() => LoadResourceTiles()); MapResources = Exts.Lazy(() => LoadResourceTiles());
TileShape = Game.modData.Manifest.TileShape;
// The Uid is calculated from the data on-disk, so // The Uid is calculated from the data on-disk, so
// format changes must be flushed to disk. // format changes must be flushed to disk.
@@ -262,9 +266,9 @@ namespace OpenRA
rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this)); rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this));
cachedTileSet = Exts.Lazy(() => Rules.TileSets[Tileset]); cachedTileSet = Exts.Lazy(() => Rules.TileSets[Tileset]);
var tl = new CPos(Bounds.Left, Bounds.Top); var tl = Map.MapToCell(TileShape, new CPos(Bounds.Left, Bounds.Top));
var br = new CPos(Bounds.Right - 1, Bounds.Bottom - 1); var br = Map.MapToCell(TileShape, new CPos(Bounds.Right - 1, Bounds.Bottom - 1));
Cells = new CellRegion(tl, br); Cells = new CellRegion(TileShape, tl, br);
CustomTerrain = new CellLayer<int>(this); CustomTerrain = new CellLayer<int>(this);
foreach (var cell in Cells) foreach (var cell in Cells)
@@ -465,17 +469,79 @@ namespace OpenRA
public bool Contains(CPos cell) 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) public CPos CellContaining(WPos pos)
{ {
if (TileShape == TileShape.Rectangle)
return new CPos(pos.X / 1024, pos.Y / 1024); 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) 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) public void ResizeCordon(int left, int top, int right, int bottom)
{ {
Bounds = Rectangle.FromLTRB(left, top, right, 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() string ComputeHash()
@@ -597,17 +666,18 @@ namespace OpenRA
return cachedTileSet.Value[GetTerrainIndex(cell)]; return cachedTileSet.Value[GetTerrainIndex(cell)];
} }
public CPos Clamp(CPos xy) public CPos Clamp(CPos cell)
{ {
var r = Bounds; var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);
return xy.Clamp(new Rectangle(r.X, r.Y, r.Width - 1, r.Height - 1)); return MapToCell(TileShape, CellToMap(TileShape, cell).Clamp(bounds));
} }
public CPos ChooseRandomCell(MersenneTwister rand) public CPos ChooseRandomCell(MersenneTwister rand)
{ {
return new CPos( var x = rand.Next(Bounds.Left, Bounds.Right);
rand.Next(Bounds.Left, Bounds.Right), var y = rand.Next(Bounds.Top, Bounds.Bottom);
rand.Next(Bounds.Top, Bounds.Bottom));
return MapToCell(TileShape, new CPos(x, y));
} }
public CPos ChooseRandomEdgeCell(MersenneTwister rand) public CPos ChooseRandomEdgeCell(MersenneTwister rand)
@@ -615,15 +685,16 @@ namespace OpenRA
var isX = rand.Next(2) == 0; var isX = rand.Next(2) == 0;
var edge = rand.Next(2) == 0; var edge = rand.Next(2) == 0;
return new CPos( var x = isX ? rand.Next(Bounds.Left, Bounds.Right) : (edge ? Bounds.Left : Bounds.Right);
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);
!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) public WRange DistanceToEdge(WPos pos, WVec dir)
{ {
var tl = CenterOfCell(new CPos(Bounds.Left, Bounds.Top)) - new WVec(512, 512, 0); var tl = CenterOfCell(Cells.TopLeft) - new WVec(512, 512, 0);
var br = CenterOfCell(new CPos(Bounds.Right, Bounds.Bottom)) + new WVec(511, 511, 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 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; 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); return new WRange(Math.Min(x, y) * dir.Length);

View File

@@ -129,9 +129,10 @@ namespace OpenRA.Widgets
tooltipContainer.Value.RemoveTooltip(); tooltipContainer.Value.RemoveTooltip();
} }
public int2 ConvertToPreview(CPos point) public int2 ConvertToPreview(CPos cell)
{ {
var preview = Preview(); var preview = Preview();
var point = Map.CellToMap(preview.Map.TileShape, cell);
var dx = (int)(previewScale * (point.X - preview.Bounds.Left)); var dx = (int)(previewScale * (point.X - preview.Bounds.Left));
var dy = (int)(previewScale * (point.Y - preview.Bounds.Top)); var dy = (int)(previewScale * (point.Y - preview.Bounds.Top));
return new int2(mapRect.X + dx, mapRect.Y + dy); return new int2(mapRect.X + dx, mapRect.Y + dy);

View File

@@ -287,7 +287,7 @@ namespace OpenRA.Mods.RA.Move
while (CellInfoPool.Count > 0) while (CellInfoPool.Count > 0)
{ {
var cellInfo = GetFromPool(); 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."); Log.Write("debug", "Discarding old pooled CellInfo of wrong size.");
continue; continue;

View File

@@ -177,19 +177,19 @@ namespace OpenRA.Mods.RA
return useExtendedIndex ? u ^ uside : u & 0x0F; 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; var u = 0;
if (p.Y == bounds.Top) u |= 0x13; if (!map.Contains(p + new CVec(0, -1))) u |= 0x13;
if (p.X == bounds.Right - 1) u |= 0x26; if (!map.Contains(p + new CVec(1, 0))) u |= 0x26;
if (p.Y == bounds.Bottom - 1) u |= 0x4C; if (!map.Contains(p + new CVec(0, 1))) u |= 0x4C;
if (p.X == bounds.Left) u |= 0x89; if (!map.Contains(p + new CVec(-1, 0))) u |= 0x89;
var uside = u & 0x0F; var uside = u & 0x0F;
if (p.X == bounds.Left && p.Y == bounds.Top) u |= 0x01; if (!map.Contains(p + new CVec(-1, -1))) u |= 0x01;
if (p.X == bounds.Right - 1 && p.Y == bounds.Top) u |= 0x02; if (!map.Contains(p + new CVec(1, -1))) u |= 0x02;
if (p.X == bounds.Right - 1 && p.Y == bounds.Bottom - 1) u |= 0x04; if (!map.Contains(p + new CVec(1, 1))) u |= 0x04;
if (p.X == bounds.Left && p.Y == bounds.Bottom - 1) u |= 0x08; if (!map.Contains(p + new CVec(-1, 1))) u |= 0x08;
return useExtendedIndex ? u ^ uside : u & 0x0F; return useExtendedIndex ? u ^ uside : u & 0x0F;
} }
@@ -197,11 +197,19 @@ namespace OpenRA.Mods.RA
public void WorldLoaded(World w, WorldRenderer wr) public void WorldLoaded(World w, WorldRenderer wr)
{ {
// Initialize tile cache // 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 screen = wr.ScreenPosition(w.Map.CenterOfCell(cell));
var variant = Game.CosmeticRandom.Next(info.ShroudVariants.Length); var variant = Game.CosmeticRandom.Next(info.ShroudVariants.Length);
tiles[cell] = new ShroudTile(cell, screen, variant); 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); fogPalette = wr.Palette(info.FogPalette);
@@ -229,7 +237,7 @@ namespace OpenRA.Mods.RA
foreach (var cell in map.Cells) foreach (var cell in map.Cells)
{ {
var t = tiles[cell]; 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.Shroud = shrouded != 0 ? shroudSprites[t.Variant * variantStride + spriteMap[shrouded]] : null;
t.Fog = shrouded != 0 ? fogSprites[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); Update(shroud);
foreach (var cell in wr.Viewport.VisibleCells) foreach (var cell in CellRegion.Expand(wr.Viewport.VisibleCells, 1))
{ {
var t = tiles[cell]; var t = tiles[cell];

View File

@@ -115,7 +115,7 @@ namespace OpenRA.Mods.RA.Widgets
var cell = MinimapPixelToCell(mi.Location); var cell = MinimapPixelToCell(mi.Location);
var pos = world.Map.CenterOfCell(cell); var pos = world.Map.CenterOfCell(cell);
if ((mi.Event == MouseInputEvent.Down || mi.Event == MouseInputEvent.Move) && mi.Button == MouseButton.Left) 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) if (mi.Event == MouseInputEvent.Down && mi.Button == MouseButton.Right)
{ {
@@ -242,8 +242,8 @@ namespace OpenRA.Mods.RA.Widgets
int2 CellToMinimapPixel(CPos p) int2 CellToMinimapPixel(CPos p)
{ {
var mapOrigin = new CPos(world.Map.Bounds.Left, world.Map.Bounds.Top); var mapOrigin = new CVec(world.Map.Bounds.Left, world.Map.Bounds.Top);
var mapOffset = p - mapOrigin; var mapOffset = Map.CellToMap(world.Map.TileShape, p) - mapOrigin;
return new int2(mapRect.X, mapRect.Y) + (previewScale * new float2(mapOffset.X, mapOffset.Y)).ToInt2(); 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 viewOrigin = new float2(mapRect.X, mapRect.Y);
var mapOrigin = new float2(world.Map.Bounds.Left, world.Map.Bounds.Top); var mapOrigin = new float2(world.Map.Bounds.Left, world.Map.Bounds.Top);
var fcell = mapOrigin + (1f / previewScale) * (p - viewOrigin); 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));
} }
} }
} }

View File

@@ -149,8 +149,8 @@ namespace OpenRA.Utility
map.Smudges = Exts.Lazy(() => new List<SmudgeReference>()); map.Smudges = Exts.Lazy(() => new List<SmudgeReference>());
map.Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()); map.Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>());
map.MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(size)); map.MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(TileShape.Rectangle, size));
map.MapTiles = Exts.Lazy(() => new CellLayer<TerrainTile>(size)); map.MapTiles = Exts.Lazy(() => new CellLayer<TerrainTile>(TileShape.Rectangle, size));
map.Options = new MapOptions(); map.Options = new MapOptions();

Binary file not shown.

View File

@@ -4,17 +4,17 @@ MapFormat: 6
RequiresMod: ts RequiresMod: ts
Title: Blank Test Map Title: Blank
Description: Test Map Description: Placeholder
Author: Reaperrr Author: Matthias Mailänder
Tileset: INTERIOR Tileset: INTERIOR
MapSize: 128,128 MapSize: 128,128
Bounds: 16,16,96,96 Bounds: 32,16,48,96
UseAsShellmap: False UseAsShellmap: False
@@ -56,53 +56,17 @@ Players:
Actors: Actors:
Actor0: mpspawn Actor0: mpspawn
Location: 23,23 Location: 50,-24
Owner: Neutral Owner: Neutral
Actor1: mpspawn Actor1: mpspawn
Location: 103,23 Location: 126,-24
Owner: Neutral
Actor2: mpspawn
Location: 88,14
Owner: Neutral
Actor3: mpspawn
Location: 88,-62
Owner: Neutral 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: Smudges:

View File

@@ -142,6 +142,7 @@ TileSets:
mods/ts/tilesets/interior.yaml mods/ts/tilesets/interior.yaml
TileSize: 48,24 TileSize: 48,24
TileShape: Diamond
Music: Music:
mods/ts/music.yaml mods/ts/music.yaml

View File

@@ -1,24 +1,24 @@
overlay: overlay:
build-valid-interior: cellsel #TODO remove backfall to RA tileset build-valid-interior: place
Start: 3 Start: 0
build-valid-snow: cellsel build-valid-snow: place
Start: 3 Start: 0
build-valid-temperat: cellsel build-valid-temperat: place
Start: 3 Start: 0
build-invalid: cellsel build-invalid: place
Start: 6 Start: 1
target-select: cellsel target-select: place
Start: 10 Start: 1
target-valid-desert: cellsel target-valid-desert: place
Start: 3 Start: 1
target-valid-interior: cellsel target-valid-interior: place
Start: 3 Start: 1
target-valid-snow: cellsel target-valid-snow: place
Start: 3 Start: 1
target-valid-temperat: cellsel target-valid-temperat: place
Start: 3 Start: 1
target-invalid: cellsel target-invalid: place
Start: 6 Start: 1
poweroff: poweroff:
offline: poweroff offline: poweroff

View File

@@ -9,7 +9,7 @@ Terrain:
TerrainType@Clear: TerrainType@Clear:
Type: Clear Type: Clear
AcceptsSmudgeType: Crater, Scorch AcceptsSmudgeType: Crater, Scorch
Color: 0, 0, 0 Color: 116, 85, 55
TargetTypes: Ground TargetTypes: Ground
TerrainType@Tiberium: TerrainType@Tiberium:
Type: Tiberium Type: Tiberium