Introduce initial PPos plumbing.

PPos is best thought of as a cell grid applied in
screen space.  Multiple cells with different
terrain heights may be projected to the same PPos,
or to multiple PPos if they do not align with the
screen grid.

PPos coordinates are used primarily for map edge
checks and shroud / visibility queries.
This commit is contained in:
Paul Chote
2015-06-23 19:41:34 +01:00
parent fb5bcd3889
commit e8794032e0
14 changed files with 381 additions and 63 deletions

View File

@@ -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();

View File

@@ -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
{

View File

@@ -61,4 +61,34 @@ namespace OpenRA
return new CPos(x, y);
}
}
/// <summary>
/// Projected map position
/// </summary>
public struct PPos : IEquatable<PPos>
{
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; }
}
}

View File

@@ -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<CellLayer<byte>> MapHeight;
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
[FieldLoader.Ignore] CellLayer<PPos[]> cellProjection;
[FieldLoader.Ignore] CellLayer<List<MPos>> inverseCellProjection;
[FieldLoader.Ignore] Lazy<TileSet> cachedTileSet;
[FieldLoader.Ignore] Lazy<Ruleset> 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<PPos, bool> containsTest;
void AssertExists(string filename)
{
using (var s = Container.GetContent(filename))
@@ -268,6 +273,8 @@ namespace OpenRA
/// </summary>
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
/// <summary>Initializes a map loaded from disk.</summary>
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<byte>(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<PPos[]>(this);
inverseCellProjection = new CellLayer<List<MPos>>(this);
// Initialize collections
foreach (var cell in AllCells)
{
var uv = cell.ToMPos(TileShape);
cellProjection[uv] = new PPos[0];
inverseCellProjection[uv] = new List<MPos>();
}
// 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<PPos>();
// 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);

View File

@@ -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<PPos>
{
// 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;
}
/// <summary>
/// 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.
/// </summary>
public MapCoordsRegion CandidateMapCoords { get { return new MapCoordsRegion(mapTopLeft, mapBottomRight); } }
public ProjectedCellRegionEnumerator GetEnumerator()
{
return new ProjectedCellRegionEnumerator(this);
}
IEnumerator<PPos> IEnumerable<PPos>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public sealed class ProjectedCellRegionEnumerator : IEnumerator<PPos>
{
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() { }
}
}
}

View File

@@ -248,6 +248,7 @@
<Compile Include="Widgets\WorldInteractionControllerWidget.cs" />
<Compile Include="Graphics\PaletteReference.cs" />
<Compile Include="Graphics\TerrainSpriteLayer.cs" />
<Compile Include="Map\ProjectedCellRegion.cs" />
<Compile Include="Map\MapCoordsRegion.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -204,7 +204,7 @@ namespace OpenRA.Traits
throw new ArgumentException("The map bounds of these shrouds do not match.", "s");
var changed = new List<CPos>();
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<CPos>();
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<CPos>();
foreach (var uv in map.CellsInsideBounds.MapCoords)
foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
{
var visible = visibleCount[uv] > 0;
if (explored[uv] != visible)

View File

@@ -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));

View File

@@ -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.

View File

@@ -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;
}
}
}

View File

@@ -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)

View File

@@ -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();

View File

@@ -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;

View File

@@ -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