diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs
index 7471b5d04f..08cecb0660 100644
--- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs
+++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs
@@ -98,8 +98,8 @@ namespace OpenRA.Graphics
var cells = restrictToBounds ? viewport.VisibleCellsInsideBounds : viewport.AllVisibleCells;
// Only draw the rows that are visible.
- var firstRow = cells.MapCoords.TopLeft.V;
- var lastRow = Math.Min(cells.MapCoords.BottomRight.V + 1, map.MapSize.Y);
+ var firstRow = cells.CandidateMapCoords.TopLeft.V.Clamp(0, map.MapSize.Y);
+ var lastRow = (cells.CandidateMapCoords.BottomRight.V + 1).Clamp(firstRow, map.MapSize.Y);
Game.Renderer.Flush();
diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs
index d67e9d37c1..d8a69b94a1 100644
--- a/OpenRA.Game/Graphics/Viewport.cs
+++ b/OpenRA.Game/Graphics/Viewport.cs
@@ -48,10 +48,10 @@ namespace OpenRA.Graphics
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
int2 viewportSize;
- CellRegion cells;
+ ProjectedCellRegion cells;
bool cellsDirty = true;
- CellRegion allCells;
+ ProjectedCellRegion allCells;
bool allCellsDirty = true;
float zoom = 1f;
@@ -93,15 +93,27 @@ namespace OpenRA.Graphics
{
worldRenderer = wr;
- var cells = wr.World.Type == WorldType.Editor ?
- map.AllCells : map.CellsInsideBounds;
-
// Calculate map bounds in world-px
- var tl = wr.ScreenPxPosition(map.CenterOfCell(cells.TopLeft) - new WVec(512, 512, 0));
- var br = wr.ScreenPxPosition(map.CenterOfCell(cells.BottomRight) + new WVec(511, 511, 0));
- mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
+ 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)
+ height /= 2;
+
+ mapBounds = new Rectangle(0, 0, width, height);
+ CenterLocation = new int2(width / 2, height / 2);
+ }
+ else
+ {
+ var tl = wr.ScreenPxPosition(map.ProjectedTopLeft);
+ var br = wr.ScreenPxPosition(map.ProjectedBottomRight);
+ mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
+ CenterLocation = (tl + br) / 2;
+ }
- CenterLocation = (tl + br) / 2;
Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1;
tileSize = Game.ModData.Manifest.TileSize;
}
@@ -209,8 +221,8 @@ namespace OpenRA.Graphics
// Visible rectangle in world coordinates (expanded to the corners of the cells)
var bounds = insideBounds ? VisibleCellsInsideBounds : AllVisibleCells;
var map = worldRenderer.World.Map;
- var ctl = map.CenterOfCell(bounds.TopLeft) - new WVec(512, 512, 0);
- var cbr = map.CenterOfCell(bounds.BottomRight) + new WVec(512, 512, 0);
+ var ctl = map.CenterOfCell(((MPos)bounds.TopLeft).ToCPos(map)) - new WVec(512, 512, 0);
+ var cbr = map.CenterOfCell(((MPos)bounds.BottomRight).ToCPos(map)) + new WVec(512, 512, 0);
// Convert to screen coordinates
var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl - new WVec(0, 0, ctl.Z))).Clamp(ScreenClip);
@@ -221,22 +233,21 @@ namespace OpenRA.Graphics
br.X + tileSize.Width, br.Y + tileSize.Height);
}
- CellRegion CalculateVisibleCells(bool insideBounds)
+ ProjectedCellRegion CalculateVisibleCells(bool insideBounds)
{
var map = worldRenderer.World.Map;
- // Calculate the viewport corners in "projected wpos" (at ground level), and
- // this to an equivalent projected cell for the two corners
- var tl = map.CellContaining(worldRenderer.ProjectedPosition(TopLeft)).ToMPos(map);
- var br = map.CellContaining(worldRenderer.ProjectedPosition(BottomRight)).ToMPos(map);
+ // Calculate the projected cell position at the corners of the visible area
+ var tl = (PPos)map.CellContaining(worldRenderer.ProjectedPosition(TopLeft)).ToMPos(map);
+ var br = (PPos)map.CellContaining(worldRenderer.ProjectedPosition(BottomRight)).ToMPos(map);
// 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)
{
- tl = new MPos(tl.U - 1, tl.V - 1);
- br = new MPos(br.U + 1, br.V + 1);
+ tl = new PPos(tl.U - 1, tl.V - 1);
+ br = new PPos(br.U + 1, br.V + 1);
}
// Clamp to the visible map bounds, if requested
@@ -246,21 +257,10 @@ namespace OpenRA.Graphics
br = map.Clamp(br);
}
- // Cells can be pushed up from below if they have non-zero height.
- // Each height step is equivalent to 512 WDist 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;
- br = new MPos(br.U, br.V + heightOffset);
-
- // Finally, make sure that this region doesn't extend outside the map area.
- tl = map.MapHeight.Value.Clamp(tl);
- br = map.MapHeight.Value.Clamp(br);
-
- return new CellRegion(map.TileShape, tl.ToCPos(map), br.ToCPos(map));
+ return new ProjectedCellRegion(map, tl, br);
}
- public CellRegion VisibleCellsInsideBounds
+ public ProjectedCellRegion VisibleCellsInsideBounds
{
get
{
@@ -274,7 +274,7 @@ namespace OpenRA.Graphics
}
}
- public CellRegion AllVisibleCells
+ public ProjectedCellRegion AllVisibleCells
{
get
{
diff --git a/OpenRA.Game/MPos.cs b/OpenRA.Game/MPos.cs
index 5dd6b862e6..32f97077a4 100644
--- a/OpenRA.Game/MPos.cs
+++ b/OpenRA.Game/MPos.cs
@@ -61,4 +61,34 @@ namespace OpenRA
return new CPos(x, y);
}
}
+
+ ///
+ /// Projected map position
+ ///
+ public struct PPos : IEquatable
+ {
+ public readonly int U, V;
+
+ public PPos(int u, int v) { U = u; V = v; }
+ public static readonly PPos Zero = new PPos(0, 0);
+
+ public static bool operator ==(PPos me, PPos other) { return me.U == other.U && me.V == other.V; }
+ public static bool operator !=(PPos me, PPos other) { return !(me == other); }
+
+ public static explicit operator MPos(PPos puv) { return new MPos(puv.U, puv.V); }
+ public static explicit operator PPos(MPos uv) { return new PPos(uv.U, uv.V); }
+
+ public PPos Clamp(Rectangle r)
+ {
+ return new PPos(Math.Min(r.Right, Math.Max(U, r.Left)),
+ Math.Min(r.Bottom, Math.Max(V, r.Top)));
+ }
+
+ public override int GetHashCode() { return U.GetHashCode() ^ V.GetHashCode(); }
+
+ public bool Equals(PPos other) { return other == this; }
+ public override bool Equals(object obj) { return obj is PPos && Equals((PPos)obj); }
+
+ public override string ToString() { return U + "," + V; }
+ }
}
\ No newline at end of file
diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs
index a61b209892..d620aa3e7f 100644
--- a/OpenRA.Game/Map/Map.cs
+++ b/OpenRA.Game/Map/Map.cs
@@ -9,6 +9,7 @@
#endregion
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
@@ -245,6 +246,8 @@ namespace OpenRA
[FieldLoader.Ignore] public Lazy> MapHeight;
[FieldLoader.Ignore] public CellLayer CustomTerrain;
+ [FieldLoader.Ignore] CellLayer cellProjection;
+ [FieldLoader.Ignore] CellLayer> inverseCellProjection;
[FieldLoader.Ignore] Lazy cachedTileSet;
[FieldLoader.Ignore] Lazy rules;
@@ -252,9 +255,11 @@ namespace OpenRA
public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
public WVec[][] CellCorners { get; private set; }
- [FieldLoader.Ignore] public CellRegion CellsInsideBounds;
+ [FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds;
[FieldLoader.Ignore] public CellRegion AllCells;
+ readonly Func containsTest;
+
void AssertExists(string filename)
{
using (var s = Container.GetContent(filename))
@@ -268,6 +273,8 @@ namespace OpenRA
///
public Map(TileSet tileset, int width, int height)
{
+ containsTest = Contains;
+
var size = new Size(width, height);
var tileShape = Game.ModData.Manifest.TileShape;
var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
@@ -307,6 +314,8 @@ namespace OpenRA
/// Initializes a map loaded from disk.
public Map(string path)
{
+ containsTest = Contains;
+
Path = path;
Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);
@@ -409,8 +418,8 @@ namespace OpenRA
var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this);
AllCells = new CellRegion(TileShape, tl, br);
- var btl = new MPos(Bounds.Left, Bounds.Top);
- var bbr = new MPos(Bounds.Right - 1, Bounds.Bottom - 1);
+ var btl = new PPos(Bounds.Left, Bounds.Top);
+ var bbr = new PPos(Bounds.Right - 1, Bounds.Bottom - 1);
SetBounds(btl, bbr);
CustomTerrain = new CellLayer(this);
@@ -428,6 +437,80 @@ namespace OpenRA
rightDelta + new WVec(0, 0, 512 * ramp[2]),
bottomDelta + new WVec(0, 0, 512 * ramp[3])
}).ToArray();
+
+ if (MaximumTerrainHeight != 0)
+ {
+ cellProjection = new CellLayer(this);
+ inverseCellProjection = new CellLayer>(this);
+
+ // Initialize collections
+ foreach (var cell in AllCells)
+ {
+ var uv = cell.ToMPos(TileShape);
+ cellProjection[uv] = new PPos[0];
+ inverseCellProjection[uv] = new List();
+ }
+
+ // Initialize projections
+ foreach (var cell in AllCells)
+ UpdateProjection(cell);
+ }
+ }
+
+ void UpdateProjection(CPos cell)
+ {
+ if (MaximumTerrainHeight == 0)
+ return;
+
+ var uv = cell.ToMPos(TileShape);
+
+ // Remove old reverse projection
+ foreach (var puv in cellProjection[uv])
+ inverseCellProjection[(MPos)puv].Remove(uv);
+
+ var projected = ProjectCellInner(uv);
+ cellProjection[uv] = projected;
+
+ foreach (var puv in projected)
+ inverseCellProjection[(MPos)puv].Add(uv);
+ }
+
+ PPos[] ProjectCellInner(MPos uv)
+ {
+ var mapHeight = MapHeight.Value;
+ if (!mapHeight.Contains(uv))
+ return NoProjectedCells;
+
+ var height = mapHeight[uv];
+ if (height == 0)
+ return new[] { (PPos)uv };
+
+ // Odd-height ramps get bumped up a level to the next even height layer
+ if ((height & 1) == 1)
+ {
+ var ti = cachedTileSet.Value.GetTileInfo(MapTiles.Value[uv]);
+ if (ti != null && ti.RampType != 0)
+ height += 1;
+ }
+
+ var candidates = new List();
+
+ // Odd-height level tiles are equally covered by four projected tiles
+ if ((height & 1) == 1)
+ {
+ if ((uv.V & 1) == 1)
+ candidates.Add(new PPos(uv.U + 1, uv.V - height));
+ else
+ candidates.Add(new PPos(uv.U - 1, uv.V - height));
+
+ candidates.Add(new PPos(uv.U, uv.V - height));
+ candidates.Add(new PPos(uv.U, uv.V - height + 1));
+ candidates.Add(new PPos(uv.U, uv.V - height - 1));
+ }
+ else
+ candidates.Add(new PPos(uv.U, uv.V - height));
+
+ return candidates.Where(c => mapHeight.Contains((MPos)c)).ToArray();
}
public Ruleset PreloadRules()
@@ -534,6 +617,8 @@ namespace OpenRA
}
}
+ tiles.CellEntryChanged += UpdateProjection;
+
return tiles;
}
@@ -552,6 +637,8 @@ namespace OpenRA
}
}
+ tiles.CellEntryChanged += UpdateProjection;
+
return tiles;
}
@@ -652,7 +739,15 @@ namespace OpenRA
public bool Contains(MPos uv)
{
- return Bounds.Contains(uv.U, uv.V);
+ // The first check ensures that the cell is within the valid map region, avoiding
+ // potential crashes in deeper code. All CellLayers have the same geometry, and
+ // CustomTerrain is convenient (cellProjection may be null and others are Lazy).
+ return CustomTerrain.Contains(uv) && ProjectedCellsCovering(uv).All(containsTest);
+ }
+
+ public bool Contains(PPos puv)
+ {
+ return Bounds.Contains(puv.U, puv.V);
}
public WPos CenterOfCell(CPos cell)
@@ -695,6 +790,39 @@ namespace OpenRA
return new CPos(u, v);
}
+ public PPos ProjectedCellCovering(WPos pos)
+ {
+ var projectedPos = pos - new WVec(0, pos.Z, pos.Z);
+ return (PPos)CellContaining(projectedPos).ToMPos(TileShape);
+ }
+
+ static readonly PPos[] NoProjectedCells = { };
+ public PPos[] ProjectedCellsCovering(MPos uv)
+ {
+ // Shortcut for mods that don't use heightmaps
+ if (MaximumTerrainHeight == 0)
+ return new[] { (PPos)uv };
+
+ if (!cellProjection.Contains(uv))
+ return NoProjectedCells;
+
+ return cellProjection[uv];
+ }
+
+ public MPos[] Unproject(PPos puv)
+ {
+ var uv = (MPos)puv;
+
+ // Shortcut for mods that don't use heightmaps
+ if (MaximumTerrainHeight == 0)
+ return new[] { uv };
+
+ if (!inverseCellProjection.Contains(uv))
+ return new MPos[0];
+
+ return inverseCellProjection[uv].ToArray();
+ }
+
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
{
return Traits.Util.GetFacing(CenterOfCell(towards) - CenterOfCell(cell), fallbackfacing);
@@ -717,12 +845,11 @@ namespace OpenRA
AllCells = new CellRegion(TileShape, tl, br);
}
- public void SetBounds(MPos tl, MPos br)
+ public void SetBounds(PPos tl, PPos br)
{
// The tl and br coordinates are inclusive, but the Rectangle
// is exclusive. Pad the right and bottom edges to match.
Bounds = Rectangle.FromLTRB(tl.U, tl.V, br.U + 1, br.V + 1);
- CellsInsideBounds = new CellRegion(TileShape, tl.ToCPos(this), br.ToCPos(this));
// Directly calculate the projected map corners in world units avoiding unnecessary
// conversions. This abuses the definition that the width of the cell is always
@@ -738,6 +865,8 @@ namespace OpenRA
ProjectedTopLeft = new WPos(tl.U * 1024, wtop, 0);
ProjectedBottomRight = new WPos(br.U * 1024 - 1, wbottom - 1, 0);
+
+ ProjectedCellBounds = new ProjectedCellRegion(this, tl, br);
}
string ComputeHash()
@@ -799,18 +928,28 @@ namespace OpenRA
public CPos Clamp(CPos cell)
{
- var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);
- return cell.ToMPos(this).Clamp(bounds).ToCPos(this);
+ return Clamp(cell.ToMPos(this)).ToCPos(this);
}
public MPos Clamp(MPos uv)
+ {
+ // Already in bounds, so don't need to do anything.
+ if (Contains(uv))
+ return uv;
+
+ // TODO: Account for terrain height
+ return (MPos)Clamp((PPos)uv);
+ }
+
+ public PPos Clamp(PPos puv)
{
var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);
- return uv.Clamp(bounds);
+ return puv.Clamp(bounds);
}
public CPos ChooseRandomCell(MersenneTwister rand)
{
+ // TODO: Account for terrain height
var x = rand.Next(Bounds.Left, Bounds.Right);
var y = rand.Next(Bounds.Top, Bounds.Bottom);
@@ -819,6 +958,7 @@ namespace OpenRA
public CPos ChooseClosestEdgeCell(CPos pos)
{
+ // TODO: Account for terrain height
var mpos = pos.ToMPos(this);
var horizontalBound = ((mpos.U - Bounds.Left) < Bounds.Width / 2) ? Bounds.Left : Bounds.Right;
@@ -832,6 +972,7 @@ namespace OpenRA
public CPos ChooseRandomEdgeCell(MersenneTwister rand)
{
+ // TODO: Account for terrain height
var isX = rand.Next(2) == 0;
var edge = rand.Next(2) == 0;
@@ -843,8 +984,10 @@ namespace OpenRA
public WDist DistanceToEdge(WPos pos, WVec dir)
{
- var tl = CenterOfCell(CellsInsideBounds.TopLeft) - new WVec(512, 512, 0);
- var br = CenterOfCell(CellsInsideBounds.BottomRight) + new WVec(511, 511, 0);
+ // TODO: Account for terrain height
+ // Project into the screen plane and then compare against ProjectedWorldBounds.
+ var tl = CenterOfCell(((MPos)ProjectedCellBounds.TopLeft).ToCPos(this)) - new WVec(512, 512, 0);
+ var br = CenterOfCell(((MPos)ProjectedCellBounds.BottomRight).ToCPos(this)) + 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 WDist(Math.Min(x, y) * dir.Length);
diff --git a/OpenRA.Game/Map/ProjectedCellRegion.cs b/OpenRA.Game/Map/ProjectedCellRegion.cs
new file mode 100644
index 0000000000..1e7b719142
--- /dev/null
+++ b/OpenRA.Game/Map/ProjectedCellRegion.cs
@@ -0,0 +1,125 @@
+#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;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace OpenRA
+{
+ // Represents a (on-screen) rectangular collection of tiles.
+ // TopLeft and BottomRight are inclusive
+ public class ProjectedCellRegion : IEnumerable
+ {
+ // Corners of the region
+ public readonly PPos TopLeft;
+ public readonly PPos BottomRight;
+
+ // Corners of the bounding map region that contains all the cells that
+ // may be projected within this region.
+ readonly MPos mapTopLeft;
+ readonly MPos mapBottomRight;
+
+ public ProjectedCellRegion(Map map, PPos topLeft, PPos bottomRight)
+ {
+ TopLeft = topLeft;
+ BottomRight = bottomRight;
+
+ // The projection from MPos -> PPos cannot produce a larger V coordinate
+ // so the top edge of the MPos region is the same as the PPos region.
+ // (in fact the cells are identical if height == 0)
+ mapTopLeft = (MPos)topLeft;
+
+ // The bottom edge is trickier: cells at MPos.V > bottomRight.V may have
+ // 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;
+
+ // 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));
+ }
+
+ public bool Contains(PPos puv)
+ {
+ return puv.U >= TopLeft.U && puv.U <= BottomRight.U && puv.V >= TopLeft.V && puv.V <= BottomRight.V;
+ }
+
+ ///
+ /// The region in map coordinates that contains all the cells that
+ /// may be projected inside this region. For increased performance,
+ /// this does not validate whether individual map cells are actually
+ /// projected inside the region.
+ ///
+ public MapCoordsRegion CandidateMapCoords { get { return new MapCoordsRegion(mapTopLeft, mapBottomRight); } }
+
+ public ProjectedCellRegionEnumerator GetEnumerator()
+ {
+ return new ProjectedCellRegionEnumerator(this);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public sealed class ProjectedCellRegionEnumerator : IEnumerator
+ {
+ readonly ProjectedCellRegion r;
+
+ // Current position, in projected map coordinates
+ int u, v;
+
+ PPos current;
+
+ public ProjectedCellRegionEnumerator(ProjectedCellRegion region)
+ {
+ r = region;
+ Reset();
+ }
+
+ public bool MoveNext()
+ {
+ u += 1;
+
+ // Check for column overflow
+ if (u > r.BottomRight.U)
+ {
+ v += 1;
+ u = r.TopLeft.U;
+
+ // Check for row overflow
+ if (v > r.BottomRight.V)
+ return false;
+ }
+
+ current = new PPos(u, v);
+ return true;
+ }
+
+ public void Reset()
+ {
+ // Enumerator starts *before* the first element in the sequence.
+ u = r.TopLeft.U - 1;
+ v = r.TopLeft.V;
+ }
+
+ public PPos Current { get { return current; } }
+ object IEnumerator.Current { get { return Current; } }
+ public void Dispose() { }
+ }
+ }
+}
diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj
index d5daabd551..dbfd7cee98 100644
--- a/OpenRA.Game/OpenRA.Game.csproj
+++ b/OpenRA.Game/OpenRA.Game.csproj
@@ -248,6 +248,7 @@
+
diff --git a/OpenRA.Game/Traits/World/Shroud.cs b/OpenRA.Game/Traits/World/Shroud.cs
index e54a8d6685..dec63f0282 100644
--- a/OpenRA.Game/Traits/World/Shroud.cs
+++ b/OpenRA.Game/Traits/World/Shroud.cs
@@ -204,7 +204,7 @@ namespace OpenRA.Traits
throw new ArgumentException("The map bounds of these shrouds do not match.", "s");
var changed = new List();
- foreach (var uv in map.CellsInsideBounds.MapCoords)
+ foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
{
if (!explored[uv] && s.explored[uv])
{
@@ -219,7 +219,7 @@ namespace OpenRA.Traits
public void ExploreAll(World world)
{
var changed = new List();
- foreach (var uv in map.CellsInsideBounds.MapCoords)
+ foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
{
if (!explored[uv])
{
@@ -234,7 +234,7 @@ namespace OpenRA.Traits
public void ResetExploration()
{
var changed = new List();
- foreach (var uv in map.CellsInsideBounds.MapCoords)
+ foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
{
var visible = visibleCount[uv] > 0;
if (explored[uv] != visible)
diff --git a/OpenRA.Mods.Common/Traits/World/PathfinderDebugOverlay.cs b/OpenRA.Mods.Common/Traits/World/PathfinderDebugOverlay.cs
index 102cc53000..3f2de61ebe 100644
--- a/OpenRA.Mods.Common/Traits/World/PathfinderDebugOverlay.cs
+++ b/OpenRA.Mods.Common/Traits/World/PathfinderDebugOverlay.cs
@@ -60,23 +60,24 @@ namespace OpenRA.Mods.Common.Traits
var doDim = refreshTick - world.WorldTick <= 0;
if (doDim) refreshTick = world.WorldTick + 20;
+ var map = wr.World.Map;
foreach (var pair in layers)
{
var c = (pair.Key != null) ? pair.Key.Color.RGB : Color.PaleTurquoise;
var layer = pair.Value;
// Only render quads in viewing range:
- foreach (var cell in wr.Viewport.VisibleCellsInsideBounds)
+ foreach (var uv in wr.Viewport.VisibleCellsInsideBounds.CandidateMapCoords)
{
- if (layer[cell] <= 0)
+ if (layer[uv] <= 0)
continue;
- var w = Math.Max(0, Math.Min(layer[cell], 128));
+ var w = Math.Max(0, Math.Min(layer[uv], 128));
if (doDim)
- layer[cell] = layer[cell] * 5 / 6;
+ layer[uv] = layer[uv] * 5 / 6;
// TODO: This doesn't make sense for isometric terrain
- var pos = wr.World.Map.CenterOfCell(cell);
+ var pos = wr.World.Map.CenterOfCell(uv.ToCPos(map));
var tl = wr.ScreenPxPosition(pos - new WVec(512, 512, 0));
var br = wr.ScreenPxPosition(pos + new WVec(511, 511, 0));
qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c));
diff --git a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs
index 6a32bfdad9..05f4e608c8 100644
--- a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs
+++ b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs
@@ -249,7 +249,9 @@ namespace OpenRA.Mods.Common.Traits
}
currentShroud = shroud;
- DirtyCells(map.CellsInsideBounds);
+ var dirty = map.ProjectedCellBounds
+ .SelectMany(puv => map.Unproject(puv).Select(uv => uv.ToCPos(map)));
+ DirtyCells(dirty);
}
// We need to update newly dirtied areas of the shroud.
diff --git a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs
index 189293d1f8..b2eb65b127 100644
--- a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs
+++ b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
var colors = wr.World.TileSet.HeightDebugColors;
var mouseCell = wr.Viewport.ViewToWorld(Viewport.LastMousePos).ToMPos(wr.World.Map);
- foreach (var uv in wr.Viewport.AllVisibleCells.MapCoords)
+ foreach (var uv in wr.Viewport.AllVisibleCells.CandidateMapCoords)
{
var height = (int)map.MapHeight.Value[uv];
var tile = map.MapTiles.Value[uv];
@@ -80,6 +80,22 @@ namespace OpenRA.Mods.Common.Traits
lr.LineWidth = 1;
}
+
+ // Projected cell coordinates for the current cell
+ var projectedCorners = map.CellCorners[0];
+ lr.LineWidth = 3;
+ foreach (var puv in map.ProjectedCellsCovering(mouseCell))
+ {
+ var pos = map.CenterOfCell(((MPos)puv).ToCPos(map));
+ var screen = projectedCorners.Select(c => wr.ScreenPxPosition(pos + c - new WVec(0, 0, pos.Z)).ToFloat2()).ToArray();
+ for (var i = 0; i < 4; i++)
+ {
+ var j = (i + 1) % 4;
+ lr.DrawLine(screen[i], screen[j], Color.Navy);
+ }
+ }
+
+ lr.LineWidth = 1;
}
}
}
diff --git a/OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs b/OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs
index 9cf4f76163..56f175f42a 100644
--- a/OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs
@@ -151,8 +151,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
Author = "Westwood Studios"
};
- var tl = new MPos(offsetX, offsetY);
- var br = new MPos(offsetX + width - 1, offsetY + height - 1);
+ var tl = new PPos(offsetX, offsetY);
+ var br = new PPos(offsetX + width - 1, offsetY + height - 1);
map.SetBounds(tl, br);
if (legacyMapFormat == IniMapFormat.RedAlert)
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs
index a3f11d14e5..185d39c274 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs
@@ -64,8 +64,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var tileset = modRules.TileSets[tilesetDropDown.Text];
var map = new Map(tileset, width + 2, height + maxTerrainHeight + 2);
- var tl = new MPos(1, 1);
- var br = new MPos(width, height + maxTerrainHeight);
+ var tl = new PPos(1, 1);
+ var br = new PPos(width, height + maxTerrainHeight);
map.SetBounds(tl, br);
map.PlayerDefinitions = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length).ToMiniYaml();
diff --git a/OpenRA.Mods.Common/Widgets/RadarWidget.cs b/OpenRA.Mods.Common/Widgets/RadarWidget.cs
index 615eda1977..742e02b0a5 100644
--- a/OpenRA.Mods.Common/Widgets/RadarWidget.cs
+++ b/OpenRA.Mods.Common/Widgets/RadarWidget.cs
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Widgets
actorSprite = new Sprite(radarSheet, new Rectangle(0, height, width, height), TextureChannel.Alpha);
// Set initial terrain data
- foreach (var cell in world.Map.CellsInsideBounds)
+ foreach (var cell in world.Map.AllCells)
UpdateTerrainCell(cell);
world.Map.MapTiles.Value.CellEntryChanged += UpdateTerrainCell;
@@ -290,7 +290,7 @@ namespace OpenRA.Mods.Common.Widgets
if (newRenderShroud != null)
{
// Redraw the full shroud sprite
- MarkShroudDirty(world.Map.CellsInsideBounds);
+ MarkShroudDirty(world.Map.AllCells);
// Update the notification binding
newRenderShroud.CellsChanged += MarkShroudDirty;
diff --git a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs
index 2ca753a292..46a1bb7db7 100644
--- a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs
+++ b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs
@@ -315,8 +315,8 @@ namespace OpenRA.Mods.D2k.UtilityCommands
Author = "Westwood Studios"
};
- var tl = new MPos(MapCordonWidth, MapCordonWidth);
- var br = new MPos(MapCordonWidth + mapSize.Width - 1, MapCordonWidth + mapSize.Height - 1);
+ var tl = new PPos(MapCordonWidth, MapCordonWidth);
+ var br = new PPos(MapCordonWidth + mapSize.Width - 1, MapCordonWidth + mapSize.Height - 1);
map.SetBounds(tl, br);
// Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset