Merge pull request #5629 from pchote/celllayers
Introduce CellLayer and CellRegion abstractions.
This commit is contained in:
@@ -37,16 +37,15 @@ namespace OpenRA.Editor
|
|||||||
for (var u = 0; u < template.Size.X; u++)
|
for (var u = 0; u < template.Size.X; u++)
|
||||||
for (var v = 0; v < template.Size.Y; v++)
|
for (var v = 0; v < template.Size.Y; v++)
|
||||||
{
|
{
|
||||||
if (surface.Map.IsInMap(new CVec(u, v) + pos))
|
var cell = pos + new CVec(u, v);
|
||||||
|
if (surface.Map.Contains(cell))
|
||||||
{
|
{
|
||||||
var z = u + v * template.Size.X;
|
var z = u + v * template.Size.X;
|
||||||
if (tile[z].Length > 0)
|
if (tile[z].Length > 0)
|
||||||
surface.Map.MapTiles.Value[u + pos.X, v + pos.Y] =
|
|
||||||
new TileReference<ushort, byte>
|
|
||||||
{
|
{
|
||||||
Type = brushTemplate.N,
|
var index = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z;
|
||||||
Index = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z,
|
surface.Map.MapTiles.Value[cell] = new TerrainTile(brushTemplate.N, index);
|
||||||
};
|
}
|
||||||
|
|
||||||
var ch = new int2((pos.X + u) / Surface.ChunkSize, (pos.Y + v) / Surface.ChunkSize);
|
var ch = new int2((pos.X + u) / Surface.ChunkSize, (pos.Y + v) / Surface.ChunkSize);
|
||||||
if (surface.Chunks.ContainsKey(ch))
|
if (surface.Chunks.ContainsKey(ch))
|
||||||
@@ -70,14 +69,15 @@ namespace OpenRA.Editor
|
|||||||
void FloodFillWithBrush(Surface s, CPos pos)
|
void FloodFillWithBrush(Surface s, CPos pos)
|
||||||
{
|
{
|
||||||
var queue = new Queue<CPos>();
|
var queue = new Queue<CPos>();
|
||||||
var replace = s.Map.MapTiles.Value[pos.X, pos.Y];
|
var replace = s.Map.MapTiles.Value[pos];
|
||||||
var touched = new bool[s.Map.MapSize.X, s.Map.MapSize.Y];
|
var touched = new bool[s.Map.MapSize.X, s.Map.MapSize.Y];
|
||||||
|
|
||||||
Action<int, int> maybeEnqueue = (x, y) =>
|
Action<int, int> maybeEnqueue = (x, y) =>
|
||||||
{
|
{
|
||||||
if (s.Map.IsInMap(x, y) && !touched[x, y])
|
var c = new CPos(x, y);
|
||||||
|
if (s.Map.Contains(c) && !touched[x, y])
|
||||||
{
|
{
|
||||||
queue.Enqueue(new CPos(x, y));
|
queue.Enqueue(c);
|
||||||
touched[x, y] = true;
|
touched[x, y] = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -86,7 +86,7 @@ namespace OpenRA.Editor
|
|||||||
while (queue.Count > 0)
|
while (queue.Count > 0)
|
||||||
{
|
{
|
||||||
var p = queue.Dequeue();
|
var p = queue.Dequeue();
|
||||||
if (s.Map.MapTiles.Value[p.X, p.Y].Type != replace.Type)
|
if (s.Map.MapTiles.Value[p].Type != replace.Type)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var a = FindEdge(s, p, new CVec(-1, 0), replace);
|
var a = FindEdge(s, p, new CVec(-1, 0), replace);
|
||||||
@@ -94,10 +94,10 @@ namespace OpenRA.Editor
|
|||||||
|
|
||||||
for (var x = a.X; x <= b.X; x++)
|
for (var x = a.X; x <= b.X; x++)
|
||||||
{
|
{
|
||||||
s.Map.MapTiles.Value[x, p.Y] = new TileReference<ushort, byte> { Type = brushTemplate.N, Index = (byte)0 };
|
s.Map.MapTiles.Value[new CPos(x, p.Y)] = new TerrainTile(brushTemplate.N, (byte)0);
|
||||||
if (s.Map.MapTiles.Value[x, p.Y - 1].Type == replace.Type)
|
if (s.Map.MapTiles.Value[new CPos(x, p.Y - 1)].Type == replace.Type)
|
||||||
maybeEnqueue(x, p.Y - 1);
|
maybeEnqueue(x, p.Y - 1);
|
||||||
if (s.Map.MapTiles.Value[x, p.Y + 1].Type == replace.Type)
|
if (s.Map.MapTiles.Value[new CPos(x, p.Y + 1)].Type == replace.Type)
|
||||||
maybeEnqueue(x, p.Y + 1);
|
maybeEnqueue(x, p.Y + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,13 +107,13 @@ namespace OpenRA.Editor
|
|||||||
s.Chunks.Clear();
|
s.Chunks.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static CPos FindEdge(Surface s, CPos p, CVec d, TileReference<ushort, byte> replace)
|
static CPos FindEdge(Surface s, CPos p, CVec d, TerrainTile replace)
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
var q = p + d;
|
var q = p + d;
|
||||||
if (!s.Map.IsInMap(q)) return p;
|
if (!s.Map.Contains(q)) return p;
|
||||||
if (s.Map.MapTiles.Value[q.X, q.Y].Type != replace.Type) return p;
|
if (s.Map.MapTiles.Value[q].Type != replace.Type) return p;
|
||||||
p = q;
|
p = q;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,7 +641,8 @@ namespace OpenRA.Editor
|
|||||||
for (var i = 0; i < surface1.Map.MapSize.X; i++)
|
for (var i = 0; i < surface1.Map.MapSize.X; i++)
|
||||||
for (var j = 0; j < surface1.Map.MapSize.Y; j++)
|
for (var j = 0; j < surface1.Map.MapSize.Y; j++)
|
||||||
{
|
{
|
||||||
if (surface1.Map.MapResources.Value[i, j].Type != 0)
|
var cell = new CPos(i, j);
|
||||||
|
if (surface1.Map.MapResources.Value[cell].Type != 0)
|
||||||
totalResource += GetResourceValue(i, j);
|
totalResource += GetResourceValue(i, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,9 +655,12 @@ namespace OpenRA.Editor
|
|||||||
for (var u = -1; u < 2; u++)
|
for (var u = -1; u < 2; u++)
|
||||||
for (var v = -1; v < 2; v++)
|
for (var v = -1; v < 2; v++)
|
||||||
{
|
{
|
||||||
if (!surface1.Map.IsInMap(new CPos(x + u, y + v)))
|
var cell = new CPos(x + u, y + v);
|
||||||
|
|
||||||
|
if (!surface1.Map.Contains(cell))
|
||||||
continue;
|
continue;
|
||||||
if (surface1.Map.MapResources.Value[x + u, y + v].Type == resourceType)
|
|
||||||
|
if (surface1.Map.MapResources.Value[cell].Type == resourceType)
|
||||||
++sum;
|
++sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,7 +670,7 @@ namespace OpenRA.Editor
|
|||||||
int GetResourceValue(int x, int y)
|
int GetResourceValue(int x, int y)
|
||||||
{
|
{
|
||||||
var imageLength = 0;
|
var imageLength = 0;
|
||||||
int type = surface1.Map.MapResources.Value[x, y].Type;
|
var type = surface1.Map.MapResources.Value[new CPos(x, y)].Type;
|
||||||
var template = surface1.ResourceTemplates.FirstOrDefault(a => a.Value.Info.ResourceType == type).Value;
|
var template = surface1.ResourceTemplates.FirstOrDefault(a => a.Value.Info.ResourceType == type).Value;
|
||||||
if (type == 1)
|
if (type == 1)
|
||||||
imageLength = 12;
|
imageLength = 12;
|
||||||
|
|||||||
@@ -21,12 +21,9 @@ namespace OpenRA.Editor
|
|||||||
|
|
||||||
public void Apply(Surface surface)
|
public void Apply(Surface surface)
|
||||||
{
|
{
|
||||||
surface.Map.MapResources.Value[surface.GetBrushLocation().X, surface.GetBrushLocation().Y]
|
var type = (byte)resourceTemplate.Info.ResourceType;
|
||||||
= new TileReference<byte, byte>
|
var index = (byte)random.Next(resourceTemplate.Info.MaxDensity);
|
||||||
{
|
surface.Map.MapResources.Value[surface.GetBrushLocation()] = new ResourceTile(type, index);
|
||||||
Type = (byte)resourceTemplate.Info.ResourceType,
|
|
||||||
Index = (byte)random.Next(resourceTemplate.Info.MaxDensity)
|
|
||||||
};
|
|
||||||
|
|
||||||
var ch = new int2(surface.GetBrushLocation().X / Surface.ChunkSize,
|
var ch = new int2(surface.GetBrushLocation().X / Surface.ChunkSize,
|
||||||
surface.GetBrushLocation().Y / Surface.ChunkSize);
|
surface.GetBrushLocation().Y / Surface.ChunkSize);
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ namespace OpenRA.Editor
|
|||||||
public bool ShowRuler;
|
public bool ShowRuler;
|
||||||
|
|
||||||
public bool IsPaste { get { return TileSelection != null && ResourceSelection != null; } }
|
public bool IsPaste { get { return TileSelection != null && ResourceSelection != null; } }
|
||||||
public TileReference<ushort, byte>[,] TileSelection;
|
public TerrainTile[,] TileSelection;
|
||||||
public TileReference<byte, byte>[,] ResourceSelection;
|
public ResourceTile[,] ResourceSelection;
|
||||||
public CPos SelectionStart;
|
public CPos SelectionStart;
|
||||||
public CPos SelectionEnd;
|
public CPos SelectionEnd;
|
||||||
|
|
||||||
@@ -206,9 +206,9 @@ namespace OpenRA.Editor
|
|||||||
var key = Map.Actors.Value.FirstOrDefault(a => a.Value.Location() == brushLocation);
|
var key = Map.Actors.Value.FirstOrDefault(a => a.Value.Location() == brushLocation);
|
||||||
if (key.Key != null) Map.Actors.Value.Remove(key.Key);
|
if (key.Key != null) Map.Actors.Value.Remove(key.Key);
|
||||||
|
|
||||||
if (Map.MapResources.Value[brushLocation.X, brushLocation.Y].Type != 0)
|
if (Map.MapResources.Value[brushLocation].Type != 0)
|
||||||
{
|
{
|
||||||
Map.MapResources.Value[brushLocation.X, brushLocation.Y] = new TileReference<byte, byte>();
|
Map.MapResources.Value[brushLocation] = new ResourceTile(0, 0);
|
||||||
var ch = new int2(brushLocation.X / ChunkSize, brushLocation.Y / ChunkSize);
|
var ch = new int2(brushLocation.X / ChunkSize, brushLocation.Y / ChunkSize);
|
||||||
if (Chunks.ContainsKey(ch))
|
if (Chunks.ContainsKey(ch))
|
||||||
{
|
{
|
||||||
@@ -271,7 +271,8 @@ namespace OpenRA.Editor
|
|||||||
for (var i = 0; i < ChunkSize; i++)
|
for (var i = 0; i < ChunkSize; i++)
|
||||||
for (var j = 0; j < ChunkSize; j++)
|
for (var j = 0; j < ChunkSize; j++)
|
||||||
{
|
{
|
||||||
var tr = Map.MapTiles.Value[u * ChunkSize + i, v * ChunkSize + j];
|
var cell = new CPos(u * ChunkSize + i, v * ChunkSize + j);
|
||||||
|
var tr = Map.MapTiles.Value[cell];
|
||||||
var tile = TileSetRenderer.Data(tr.Type);
|
var tile = TileSetRenderer.Data(tr.Type);
|
||||||
var index = (tr.Index < tile.Count) ? tr.Index : (byte)0;
|
var index = (tr.Index < tile.Count) ? tr.Index : (byte)0;
|
||||||
var rawImage = tile[index];
|
var rawImage = tile[index];
|
||||||
@@ -279,9 +280,9 @@ namespace OpenRA.Editor
|
|||||||
for (var y = 0; y < TileSetRenderer.TileSize; y++)
|
for (var y = 0; y < TileSetRenderer.TileSize; y++)
|
||||||
p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = Palette.GetColor(rawImage[x + TileSetRenderer.TileSize * y]).ToArgb();
|
p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = Palette.GetColor(rawImage[x + TileSetRenderer.TileSize * y]).ToArgb();
|
||||||
|
|
||||||
if (Map.MapResources.Value[u * ChunkSize + i, v * ChunkSize + j].Type != 0)
|
if (Map.MapResources.Value[cell].Type != 0)
|
||||||
{
|
{
|
||||||
var resourceImage = ResourceTemplates[Map.MapResources.Value[u * ChunkSize + i, v * ChunkSize + j].Type].Bitmap;
|
var resourceImage = ResourceTemplates[Map.MapResources.Value[cell].Type].Bitmap;
|
||||||
var srcdata = resourceImage.LockBits(resourceImage.Bounds(),
|
var srcdata = resourceImage.LockBits(resourceImage.Bounds(),
|
||||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||||
|
|
||||||
@@ -508,16 +509,17 @@ namespace OpenRA.Editor
|
|||||||
var width = Math.Abs((start - end).X);
|
var width = Math.Abs((start - end).X);
|
||||||
var height = Math.Abs((start - end).Y);
|
var height = Math.Abs((start - end).Y);
|
||||||
|
|
||||||
TileSelection = new TileReference<ushort, byte>[width, height];
|
TileSelection = new TerrainTile[width, height];
|
||||||
ResourceSelection = new TileReference<byte, byte>[width, height];
|
ResourceSelection = new ResourceTile[width, height];
|
||||||
|
|
||||||
for (var x = 0; x < width; x++)
|
for (var x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
for (var y = 0; y < height; y++)
|
for (var y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
// TODO: crash prevention
|
// TODO: crash prevention
|
||||||
TileSelection[x, y] = Map.MapTiles.Value[start.X + x, start.Y + y];
|
var cell = new CPos(start.X + x, start.Y + y);
|
||||||
ResourceSelection[x, y] = Map.MapResources.Value[start.X + x, start.Y + y];
|
TileSelection[x, y] = Map.MapTiles.Value[cell];
|
||||||
|
ResourceSelection[x, y] = Map.MapResources.Value[cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,10 +536,11 @@ namespace OpenRA.Editor
|
|||||||
{
|
{
|
||||||
var mapX = loc.X + x;
|
var mapX = loc.X + x;
|
||||||
var mapY = loc.Y + y;
|
var mapY = loc.Y + y;
|
||||||
|
var cell = new CPos(mapX, mapY);
|
||||||
|
|
||||||
// TODO: crash prevention for outside of bounds
|
// TODO: crash prevention for outside of bounds
|
||||||
Map.MapTiles.Value[mapX, mapY] = TileSelection[x, y];
|
Map.MapTiles.Value[cell] = TileSelection[x, y];
|
||||||
Map.MapResources.Value[mapX, mapY] = ResourceSelection[x, y];
|
Map.MapResources.Value[cell] = ResourceSelection[x, y];
|
||||||
|
|
||||||
var ch = new int2(mapX / ChunkSize, mapY / ChunkSize);
|
var ch = new int2(mapX / ChunkSize, mapY / ChunkSize);
|
||||||
if (Chunks.ContainsKey(ch))
|
if (Chunks.ContainsKey(ch))
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ namespace OpenRA.GameRules
|
|||||||
if (target.Type == TargetType.Terrain)
|
if (target.Type == TargetType.Terrain)
|
||||||
{
|
{
|
||||||
var cell = target.CenterPosition.ToCPos();
|
var cell = target.CenterPosition.ToCPos();
|
||||||
if (!world.Map.IsInMap(cell))
|
if (!world.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var cellInfo = world.Map.GetTerrainInfo(cell);
|
var cellInfo = world.Map.GetTerrainInfo(cell);
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ namespace OpenRA.Graphics
|
|||||||
var res = resourceRules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>()
|
var res = resourceRules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>()
|
||||||
.Where(t => t.ResourceType == map.MapResources.Value[mapX, mapY].Type)
|
.Where(t => t.ResourceType == map.MapResources.Value[mapX, mapY].Type)
|
||||||
.Select(t => t.TerrainType).FirstOrDefault();
|
.Select(t => t.TerrainType).FirstOrDefault();
|
||||||
|
|
||||||
if (res == null)
|
if (res == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ 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.IsInMap(cell))
|
if (world.Map.Contains(cell))
|
||||||
*(c + ((cell.Y - world.Map.Bounds.Top) * bitmapData.Stride >> 2) + cell.X - world.Map.Bounds.Left) = color.ToArgb();
|
*(c + ((cell.Y - world.Map.Bounds.Top) * bitmapData.Stride >> 2) + cell.X - world.Map.Bounds.Left) = color.ToArgb();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,16 +28,13 @@ namespace OpenRA.Graphics
|
|||||||
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
|
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
|
||||||
var nv = 0;
|
var nv = 0;
|
||||||
|
|
||||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
foreach (var cell in map.Cells)
|
||||||
{
|
{
|
||||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
var tile = wr.Theater.TileSprite(map.MapTiles.Value[cell]);
|
||||||
{
|
var pos = wr.ScreenPosition(cell.CenterPosition) - 0.5f * tile.size;
|
||||||
var tile = wr.Theater.TileSprite(map.MapTiles.Value[i, j]);
|
|
||||||
var pos = wr.ScreenPosition(new CPos(i, j).CenterPosition) - 0.5f * tile.size;
|
|
||||||
Util.FastCreateQuad(vertices, pos, tile, terrainPalette, nv, tile.size);
|
Util.FastCreateQuad(vertices, pos, tile, terrainPalette, nv, tile.size);
|
||||||
nv += 4;
|
nv += 4;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
|
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
|
||||||
vertexBuffer.SetData(vertices, nv);
|
vertexBuffer.SetData(vertices, nv);
|
||||||
@@ -46,9 +43,9 @@ namespace OpenRA.Graphics
|
|||||||
public void Draw(WorldRenderer wr, Viewport viewport)
|
public void Draw(WorldRenderer wr, Viewport viewport)
|
||||||
{
|
{
|
||||||
var verticesPerRow = 4*map.Bounds.Width;
|
var verticesPerRow = 4*map.Bounds.Width;
|
||||||
var bounds = viewport.CellBounds;
|
var cells = viewport.VisibleCells;
|
||||||
var firstRow = bounds.Top - map.Bounds.Top;
|
var firstRow = cells.TopLeft.Y - map.Bounds.Top;
|
||||||
var lastRow = bounds.Bottom - map.Bounds.Top;
|
var lastRow = cells.BottomRight.Y - map.Bounds.Top + 1;
|
||||||
|
|
||||||
if (lastRow < 0 || firstRow > map.Bounds.Height)
|
if (lastRow < 0 || firstRow > map.Bounds.Height)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace OpenRA.Graphics
|
|||||||
missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1));
|
missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sprite TileSprite(TileReference<ushort, byte> r)
|
public Sprite TileSprite(TerrainTile r)
|
||||||
{
|
{
|
||||||
Sprite[] template;
|
Sprite[] template;
|
||||||
if (!templates.TryGetValue(r.Type, out template))
|
if (!templates.TryGetValue(r.Type, out template))
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -46,7 +47,8 @@ namespace OpenRA.Graphics
|
|||||||
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
|
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
|
||||||
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
|
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
|
||||||
int2 viewportSize;
|
int2 viewportSize;
|
||||||
bool cellBoundsDirty = true;
|
CellRegion cells;
|
||||||
|
bool cellsDirty = true;
|
||||||
|
|
||||||
float zoom = 1f;
|
float zoom = 1f;
|
||||||
public float Zoom
|
public float Zoom
|
||||||
@@ -60,7 +62,7 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
zoom = value;
|
zoom = value;
|
||||||
viewportSize = (1f / zoom * new float2(Game.Renderer.Resolution)).ToInt2();
|
viewportSize = (1f / zoom * new float2(Game.Renderer.Resolution)).ToInt2();
|
||||||
cellBoundsDirty = true;
|
cellsDirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,14 +112,14 @@ namespace OpenRA.Graphics
|
|||||||
public void Center(WPos pos)
|
public void Center(WPos pos)
|
||||||
{
|
{
|
||||||
CenterLocation = worldRenderer.ScreenPxPosition(pos).Clamp(mapBounds);
|
CenterLocation = worldRenderer.ScreenPxPosition(pos).Clamp(mapBounds);
|
||||||
cellBoundsDirty = true;
|
cellsDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Scroll(float2 delta, bool ignoreBorders)
|
public void Scroll(float2 delta, bool ignoreBorders)
|
||||||
{
|
{
|
||||||
// Convert scroll delta from world-px to viewport-px
|
// Convert scroll delta from world-px to viewport-px
|
||||||
CenterLocation += (1f / Zoom * delta).ToInt2();
|
CenterLocation += (1f / Zoom * delta).ToInt2();
|
||||||
cellBoundsDirty = true;
|
cellsDirty = true;
|
||||||
|
|
||||||
if (!ignoreBorders)
|
if (!ignoreBorders)
|
||||||
CenterLocation = CenterLocation.Clamp(mapBounds);
|
CenterLocation = CenterLocation.Clamp(mapBounds);
|
||||||
@@ -129,33 +131,30 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var r = CellBounds;
|
var ctl = VisibleCells.TopLeft.TopLeft;
|
||||||
var ctl = new CPos(r.Left, r.Top).TopLeft;
|
var cbr = VisibleCells.BottomRight.BottomRight;
|
||||||
var cbr = new CPos(r.Right, r.Bottom).TopLeft;
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rectangle (in cell coords) of cells that are currently visible on the screen
|
public CellRegion VisibleCells
|
||||||
Rectangle cachedRect;
|
|
||||||
public Rectangle CellBounds
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (cellBoundsDirty)
|
if (cellsDirty)
|
||||||
{
|
{
|
||||||
var boundary = new CVec(1, 1);
|
// Calculate the intersection of the visible rectangle and the map.
|
||||||
var tl = worldRenderer.Position(TopLeft).ToCPos() - boundary;
|
var map = worldRenderer.world.Map;
|
||||||
var br = worldRenderer.Position(BottomRight).ToCPos() + boundary;
|
var tl = map.Clamp(worldRenderer.Position(TopLeft).ToCPos() - new CVec(1, 1));
|
||||||
|
var br = map.Clamp(worldRenderer.Position(BottomRight).ToCPos());
|
||||||
|
|
||||||
cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), worldRenderer.world.Map.Bounds);
|
cells = new CellRegion(tl, br);
|
||||||
cellBoundsDirty = false;
|
cellsDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var b = worldRenderer.world.VisibleBounds;
|
return cells;
|
||||||
return b.HasValue ? Rectangle.Intersect(cachedRect, b.Value) : cachedRect;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
OpenRA.Game/Map/CellLayer.cs
Normal file
105
OpenRA.Game/Map/CellLayer.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2014 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.Drawing;
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
|
||||||
|
namespace OpenRA
|
||||||
|
{
|
||||||
|
// Represents a layer of "something" that covers the map
|
||||||
|
public class CellLayer<T> : IEnumerable<T>
|
||||||
|
{
|
||||||
|
public readonly Size Size;
|
||||||
|
T[] entries;
|
||||||
|
|
||||||
|
public CellLayer(Map map)
|
||||||
|
: this(new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||||
|
|
||||||
|
public CellLayer(Size size)
|
||||||
|
{
|
||||||
|
Size = size;
|
||||||
|
entries = new T[size.Width * size.Height];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve an array index from cell coordinates
|
||||||
|
int Index(CPos cell)
|
||||||
|
{
|
||||||
|
// This will eventually define a distinct case for diagonal cell grids
|
||||||
|
return cell.Y * Size.Width + cell.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the <see cref="OpenRA.CellLayer"/> using cell coordinates</summary>
|
||||||
|
public T this[CPos cell]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return entries[Index(cell)];
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
entries[Index(cell)] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the layer contents using raw map coordinates (not CPos!)</summary>
|
||||||
|
public T this[int u, int v]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return entries[v * Size.Width + u];
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
entries[v * Size.Width + u] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Clears the layer contents with a known value</summary>
|
||||||
|
public void Clear(T clearValue)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < entries.Length; i++)
|
||||||
|
entries[i] = clearValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
return (IEnumerator<T>)entries.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
public static class CellLayer
|
||||||
|
{
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
var result = new CellLayer<T>(newSize);
|
||||||
|
var width = Math.Min(layer.Size.Width, newSize.Width);
|
||||||
|
var height = Math.Min(layer.Size.Height, newSize.Height);
|
||||||
|
|
||||||
|
result.Clear(defaultValue);
|
||||||
|
for (var j = 0; j < height; j++)
|
||||||
|
for (var i = 0; i < width; i++)
|
||||||
|
result[i, j] = layer[i, j];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
101
OpenRA.Game/Map/CellRegion.cs
Normal file
101
OpenRA.Game/Map/CellRegion.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2014 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.Drawing;
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
|
||||||
|
namespace OpenRA
|
||||||
|
{
|
||||||
|
// Represents a (on-screen) rectangular collection of tiles.
|
||||||
|
// TopLeft and BottomRight are inclusive
|
||||||
|
public class CellRegion : IEnumerable<CPos>
|
||||||
|
{
|
||||||
|
// Corners of the region
|
||||||
|
public readonly CPos TopLeft;
|
||||||
|
public readonly CPos BottomRight;
|
||||||
|
|
||||||
|
// Corners in map coordinates
|
||||||
|
// Defined for forward compatibility with diagonal cell grids
|
||||||
|
readonly CPos mapTopLeft;
|
||||||
|
readonly CPos mapBottomRight;
|
||||||
|
|
||||||
|
public CellRegion(CPos topLeft, CPos bottomRight)
|
||||||
|
{
|
||||||
|
TopLeft = topLeft;
|
||||||
|
BottomRight = bottomRight;
|
||||||
|
|
||||||
|
mapTopLeft = TopLeft;
|
||||||
|
mapBottomRight = BottomRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(CPos cell)
|
||||||
|
{
|
||||||
|
// Defined for forward compatibility with diagonal cell grids
|
||||||
|
var uv = cell;
|
||||||
|
return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<CPos> GetEnumerator()
|
||||||
|
{
|
||||||
|
return new CellRegionEnumerator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CellRegionEnumerator : IEnumerator<CPos>
|
||||||
|
{
|
||||||
|
readonly CellRegion r;
|
||||||
|
|
||||||
|
// Current position, in map coordinates
|
||||||
|
int u, v;
|
||||||
|
|
||||||
|
public CellRegionEnumerator(CellRegion region)
|
||||||
|
{
|
||||||
|
r = region;
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
u += 1;
|
||||||
|
|
||||||
|
// Check for column overflow
|
||||||
|
if (u > r.mapBottomRight.X)
|
||||||
|
{
|
||||||
|
v += 1;
|
||||||
|
u = r.mapTopLeft.X;
|
||||||
|
|
||||||
|
// Check for row overflow
|
||||||
|
if (v > r.mapBottomRight.Y)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
// Enumerator starts *before* the first element in the sequence.
|
||||||
|
u = r.mapTopLeft.X - 1;
|
||||||
|
v = r.mapTopLeft.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CPos Current { get { return new CPos(u, v); } }
|
||||||
|
object IEnumerator.Current { get { return Current; } }
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -110,29 +111,38 @@ namespace OpenRA
|
|||||||
[FieldLoader.Ignore] public byte TileFormat = 1;
|
[FieldLoader.Ignore] public byte TileFormat = 1;
|
||||||
public int2 MapSize;
|
public int2 MapSize;
|
||||||
|
|
||||||
[FieldLoader.Ignore] public Lazy<TileReference<ushort, byte>[,]> MapTiles;
|
[FieldLoader.Ignore] public Lazy<CellLayer<TerrainTile>> MapTiles;
|
||||||
[FieldLoader.Ignore] public Lazy<TileReference<byte, byte>[,]> MapResources;
|
[FieldLoader.Ignore] public Lazy<CellLayer<ResourceTile>> MapResources;
|
||||||
[FieldLoader.Ignore] public int[,] CustomTerrain;
|
[FieldLoader.Ignore] public CellLayer<int> CustomTerrain;
|
||||||
|
|
||||||
[FieldLoader.Ignore] Lazy<Ruleset> rules;
|
[FieldLoader.Ignore] Lazy<Ruleset> rules;
|
||||||
public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
|
public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
|
||||||
public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
|
public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
|
||||||
|
|
||||||
|
[FieldLoader.Ignore] public CellRegion Cells;
|
||||||
|
|
||||||
public static Map FromTileset(TileSet tileset)
|
public static Map FromTileset(TileSet tileset)
|
||||||
{
|
{
|
||||||
var tile = tileset.Templates.First();
|
var size = new Size(1, 1);
|
||||||
var tileRef = new TileReference<ushort, byte> { Type = tile.Key, Index = (byte)0 };
|
var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
|
||||||
|
|
||||||
|
var makeMapTiles = Exts.Lazy(() =>
|
||||||
|
{
|
||||||
|
var ret = new CellLayer<TerrainTile>(size);
|
||||||
|
ret.Clear(tileRef);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
var map = new Map()
|
var map = new Map()
|
||||||
{
|
{
|
||||||
Title = "Name your map here",
|
Title = "Name your map here",
|
||||||
Description = "Describe your map here",
|
Description = "Describe your map here",
|
||||||
Author = "Your name here",
|
Author = "Your name here",
|
||||||
MapSize = new int2(1, 1),
|
MapSize = new int2(size),
|
||||||
Tileset = tileset.Id,
|
Tileset = tileset.Id,
|
||||||
Options = new MapOptions(),
|
Options = new MapOptions(),
|
||||||
MapResources = Exts.Lazy(() => new TileReference<byte, byte>[1, 1]),
|
MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(size)),
|
||||||
MapTiles = Exts.Lazy(() => new TileReference<ushort, byte>[1, 1] { { tileRef } }),
|
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>())
|
||||||
};
|
};
|
||||||
@@ -229,11 +239,6 @@ namespace OpenRA
|
|||||||
NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications");
|
NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications");
|
||||||
TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations");
|
TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations");
|
||||||
|
|
||||||
CustomTerrain = new int[MapSize.X, MapSize.Y];
|
|
||||||
for (var x = 0; x < MapSize.X; x++)
|
|
||||||
for (var y = 0; y < MapSize.Y; y++)
|
|
||||||
CustomTerrain[x, y] = -1;
|
|
||||||
|
|
||||||
MapTiles = Exts.Lazy(() => LoadMapTiles());
|
MapTiles = Exts.Lazy(() => LoadMapTiles());
|
||||||
MapResources = Exts.Lazy(() => LoadResourceTiles());
|
MapResources = Exts.Lazy(() => LoadResourceTiles());
|
||||||
|
|
||||||
@@ -254,6 +259,14 @@ namespace OpenRA
|
|||||||
void PostInit()
|
void PostInit()
|
||||||
{
|
{
|
||||||
rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this));
|
rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this));
|
||||||
|
|
||||||
|
var tl = new CPos(Bounds.Left, Bounds.Top);
|
||||||
|
var br = new CPos(Bounds.Right - 1, Bounds.Bottom - 1);
|
||||||
|
Cells = new CellRegion(tl, br);
|
||||||
|
|
||||||
|
CustomTerrain = new CellLayer<int>(this);
|
||||||
|
foreach (var cell in Cells)
|
||||||
|
CustomTerrain[cell] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Ruleset PreloadRules()
|
public Ruleset PreloadRules()
|
||||||
@@ -346,9 +359,9 @@ namespace OpenRA
|
|||||||
Container.Write(entries);
|
Container.Write(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileReference<ushort, byte>[,] LoadMapTiles()
|
public CellLayer<TerrainTile> LoadMapTiles()
|
||||||
{
|
{
|
||||||
var tiles = new TileReference<ushort, byte>[MapSize.X, MapSize.Y];
|
var tiles = new CellLayer<TerrainTile>(this);
|
||||||
using (var dataStream = Container.GetContent("map.bin"))
|
using (var dataStream = Container.GetContent("map.bin"))
|
||||||
{
|
{
|
||||||
if (dataStream.ReadUInt8() != 1)
|
if (dataStream.ReadUInt8() != 1)
|
||||||
@@ -365,23 +378,27 @@ namespace OpenRA
|
|||||||
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3);
|
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3);
|
||||||
var d = 0;
|
var d = 0;
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
|
{
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
{
|
{
|
||||||
var tile = BitConverter.ToUInt16(data, d);
|
var tile = BitConverter.ToUInt16(data, d);
|
||||||
d += 2;
|
d += 2;
|
||||||
|
|
||||||
var index = data[d++];
|
var index = data[d++];
|
||||||
if (index == byte.MaxValue)
|
if (index == byte.MaxValue)
|
||||||
index = (byte)(i % 4 + (j % 4) * 4);
|
index = (byte)(i % 4 + (j % 4) * 4);
|
||||||
tiles[i, j] = new TileReference<ushort, byte>(tile, index);
|
|
||||||
|
tiles[i, j] = new TerrainTile(tile, index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileReference<byte, byte>[,] LoadResourceTiles()
|
public CellLayer<ResourceTile> LoadResourceTiles()
|
||||||
{
|
{
|
||||||
var resources = new TileReference<byte, byte>[MapSize.X, MapSize.Y];
|
var resources = new CellLayer<ResourceTile>(this);
|
||||||
|
|
||||||
using (var dataStream = Container.GetContent("map.bin"))
|
using (var dataStream = Container.GetContent("map.bin"))
|
||||||
{
|
{
|
||||||
@@ -400,10 +417,11 @@ namespace OpenRA
|
|||||||
|
|
||||||
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2);
|
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2);
|
||||||
var d = 0;
|
var d = 0;
|
||||||
|
|
||||||
// Load resource data
|
// Load resource data
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
resources[i, j] = new TileReference<byte, byte>(data[d++], data[d++]);
|
resources[i, j] = new ResourceTile(data[d++], data[d++]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resources;
|
return resources;
|
||||||
@@ -423,38 +441,43 @@ namespace OpenRA
|
|||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
{
|
{
|
||||||
writer.Write(MapTiles.Value[i, j].Type);
|
var tile = MapTiles.Value[new CPos(i, j)];
|
||||||
writer.Write(MapTiles.Value[i, j].Index);
|
writer.Write(tile.Type);
|
||||||
|
writer.Write(tile.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resource data
|
// Resource data
|
||||||
for (var i = 0; i < MapSize.X; i++)
|
for (var i = 0; i < MapSize.X; i++)
|
||||||
|
{
|
||||||
for (var j = 0; j < MapSize.Y; j++)
|
for (var j = 0; j < MapSize.Y; j++)
|
||||||
{
|
{
|
||||||
writer.Write(MapResources.Value[i, j].Type);
|
var tile = MapResources.Value[new CPos(i, j)];
|
||||||
writer.Write(MapResources.Value[i, j].Index);
|
writer.Write(tile.Type);
|
||||||
|
writer.Write(tile.Index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataStream.ToArray();
|
return dataStream.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsInMap(CPos xy) { return IsInMap(xy.X, xy.Y); }
|
public bool Contains(CPos xy) { return Bounds.Contains(xy.X, xy.Y); }
|
||||||
public bool IsInMap(int x, int y) { return Bounds.Contains(x, y); }
|
|
||||||
|
|
||||||
public void Resize(int width, int height) // editor magic.
|
public void Resize(int width, int height) // editor magic.
|
||||||
{
|
{
|
||||||
var oldMapTiles = MapTiles.Value;
|
var oldMapTiles = MapTiles.Value;
|
||||||
var oldMapResources = MapResources.Value;
|
var oldMapResources = MapResources.Value;
|
||||||
|
var newSize = new Size(width, height);
|
||||||
|
|
||||||
MapTiles = Exts.Lazy(() => Exts.ResizeArray(oldMapTiles, oldMapTiles[0, 0], width, height));
|
MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0]));
|
||||||
MapResources = Exts.Lazy(() => Exts.ResizeArray(oldMapResources, oldMapResources[0, 0], width, height));
|
MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0]));
|
||||||
MapSize = new int2(width, height);
|
MapSize = new int2(newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
string ComputeHash()
|
string ComputeHash()
|
||||||
@@ -524,26 +547,30 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
for (var i = Bounds.Left; i < Bounds.Right; i++)
|
for (var i = Bounds.Left; i < Bounds.Right; i++)
|
||||||
{
|
{
|
||||||
var tr = MapTiles.Value[i, j];
|
var cell = new CPos(i, j);
|
||||||
if (!tileset.Templates.ContainsKey(tr.Type))
|
var type = MapTiles.Value[cell].Type;
|
||||||
|
var index = MapTiles.Value[cell].Index;
|
||||||
|
if (!tileset.Templates.ContainsKey(type))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Unknown Tile ID {0}".F(tr.Type));
|
Console.WriteLine("Unknown Tile ID {0}".F(type));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var template = tileset.Templates[tr.Type];
|
|
||||||
|
var template = tileset.Templates[type];
|
||||||
if (!template.PickAny)
|
if (!template.PickAny)
|
||||||
continue;
|
continue;
|
||||||
tr.Index = (byte)r.Next(0, template.TilesCount);
|
|
||||||
MapTiles.Value[i, j] = tr;
|
index = (byte)r.Next(0, template.TilesCount);
|
||||||
|
MapTiles.Value[cell] = new TerrainTile(type, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetTerrainIndex(CPos cell)
|
public int GetTerrainIndex(CPos cell)
|
||||||
{
|
{
|
||||||
var custom = CustomTerrain[cell.X, cell.Y];
|
var custom = CustomTerrain[cell];
|
||||||
var tileSet = Rules.TileSets[Tileset];
|
var tileSet = Rules.TileSets[Tileset];
|
||||||
return custom != -1 ? custom : tileSet.GetTerrainIndex(MapTiles.Value[cell.X, cell.Y]);
|
return custom != -1 ? custom : tileSet.GetTerrainIndex(MapTiles.Value[cell]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TerrainTypeInfo GetTerrainInfo(CPos cell)
|
public TerrainTypeInfo GetTerrainInfo(CPos cell)
|
||||||
@@ -606,12 +633,12 @@ namespace OpenRA
|
|||||||
if (range >= TilesByDistance.Length)
|
if (range >= TilesByDistance.Length)
|
||||||
throw new InvalidOperationException("FindTilesInCircle supports queries for only <= {0}".F(MaxTilesInCircleRange));
|
throw new InvalidOperationException("FindTilesInCircle supports queries for only <= {0}".F(MaxTilesInCircleRange));
|
||||||
|
|
||||||
for(var i = 0; i <= range; i++)
|
for (var i = 0; i <= range; i++)
|
||||||
{
|
{
|
||||||
foreach(var offset in TilesByDistance[i])
|
foreach (var offset in TilesByDistance[i])
|
||||||
{
|
{
|
||||||
var t = offset + center;
|
var t = offset + center;
|
||||||
if (Bounds.Contains(t.X, t.Y))
|
if (Contains(t))
|
||||||
yield return t;
|
yield return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,29 @@
|
|||||||
|
|
||||||
namespace OpenRA
|
namespace OpenRA
|
||||||
{
|
{
|
||||||
public struct TileReference<T, U>
|
public struct TerrainTile
|
||||||
{
|
{
|
||||||
public T Type;
|
public readonly ushort Type;
|
||||||
public U Index;
|
public readonly byte Index;
|
||||||
|
|
||||||
public TileReference(T t, U i)
|
public TerrainTile(ushort type, byte index)
|
||||||
{
|
{
|
||||||
Type = t;
|
Type = type;
|
||||||
Index = i;
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ResourceTile
|
||||||
|
{
|
||||||
|
public readonly byte Type;
|
||||||
|
public readonly byte Index;
|
||||||
|
|
||||||
|
public ResourceTile(byte type, byte index)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); }
|
public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); }
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ namespace OpenRA
|
|||||||
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetTerrainIndex(TileReference<ushort, byte> r)
|
public int GetTerrainIndex(TerrainTile r)
|
||||||
{
|
{
|
||||||
var tpl = Templates[r.Type];
|
var tpl = Templates[r.Type];
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ namespace OpenRA
|
|||||||
root.WriteToFile(filepath);
|
root.WriteToFile(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TerrainTypeInfo GetTerrainInfo(TileReference<ushort, byte> r)
|
public TerrainTypeInfo GetTerrainInfo(TerrainTile r)
|
||||||
{
|
{
|
||||||
return terrainInfo[GetTerrainIndex(r)];
|
return terrainInfo[GetTerrainIndex(r)];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,6 +235,8 @@
|
|||||||
<Compile Include="Support\MersenneTwister.cs" />
|
<Compile Include="Support\MersenneTwister.cs" />
|
||||||
<Compile Include="GameInformation.cs" />
|
<Compile Include="GameInformation.cs" />
|
||||||
<Compile Include="Widgets\RootWidget.cs" />
|
<Compile Include="Widgets\RootWidget.cs" />
|
||||||
|
<Compile Include="Map\CellLayer.cs" />
|
||||||
|
<Compile Include="Map\CellRegion.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace OpenRA.Orders
|
|||||||
|
|
||||||
IEnumerable<Order> OrderInner(World world, CPos xy, MouseInput mi)
|
IEnumerable<Order> OrderInner(World world, CPos xy, MouseInput mi)
|
||||||
{
|
{
|
||||||
if (mi.Button == expectedButton && world.Map.IsInMap(xy))
|
if (mi.Button == expectedButton && world.Map.Contains(xy))
|
||||||
{
|
{
|
||||||
world.CancelInputMode();
|
world.CancelInputMode();
|
||||||
foreach (var subject in subjects)
|
foreach (var subject in subjects)
|
||||||
@@ -66,6 +66,6 @@ namespace OpenRA.Orders
|
|||||||
public virtual void Tick(World world) { }
|
public virtual void Tick(World world) { }
|
||||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
||||||
public void RenderAfterWorld(WorldRenderer wr, World world) { }
|
public void RenderAfterWorld(WorldRenderer wr, World world) { }
|
||||||
public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
|
public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
readonly ActorMapInfo info;
|
readonly ActorMapInfo info;
|
||||||
readonly Map map;
|
readonly Map map;
|
||||||
InfluenceNode[,] influence;
|
readonly CellLayer<InfluenceNode> influence;
|
||||||
|
|
||||||
List<Actor>[] actors;
|
List<Actor>[] actors;
|
||||||
int rows, cols;
|
int rows, cols;
|
||||||
@@ -56,7 +56,7 @@ namespace OpenRA.Traits
|
|||||||
{
|
{
|
||||||
this.info = info;
|
this.info = info;
|
||||||
map = world.Map;
|
map = world.Map;
|
||||||
influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y];
|
influence = new CellLayer<InfluenceNode>(world.Map);
|
||||||
|
|
||||||
cols = world.Map.MapSize.X / info.BinSize + 1;
|
cols = world.Map.MapSize.X / info.BinSize + 1;
|
||||||
rows = world.Map.MapSize.Y / info.BinSize + 1;
|
rows = world.Map.MapSize.Y / info.BinSize + 1;
|
||||||
@@ -71,20 +71,20 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public IEnumerable<Actor> GetUnitsAt(CPos a)
|
public IEnumerable<Actor> GetUnitsAt(CPos a)
|
||||||
{
|
{
|
||||||
if (!map.IsInMap(a))
|
if (!map.Contains(a))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
for (var i = influence[a.X, a.Y]; i != null; i = i.Next)
|
for (var i = influence[a]; i != null; i = i.Next)
|
||||||
if (!i.Actor.Destroyed)
|
if (!i.Actor.Destroyed)
|
||||||
yield return i.Actor;
|
yield return i.Actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
|
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
|
||||||
{
|
{
|
||||||
if (!map.IsInMap(a))
|
if (!map.Contains(a))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
for (var i = influence[a.X, a.Y]; i != null; i = i.Next)
|
for (var i = influence[a]; i != null; i = i.Next)
|
||||||
if (!i.Actor.Destroyed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
|
if (!i.Actor.Destroyed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
|
||||||
yield return i.Actor;
|
yield return i.Actor;
|
||||||
}
|
}
|
||||||
@@ -107,12 +107,12 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public bool AnyUnitsAt(CPos a)
|
public bool AnyUnitsAt(CPos a)
|
||||||
{
|
{
|
||||||
return influence[a.X, a.Y] != null;
|
return influence[a] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AnyUnitsAt(CPos a, SubCell sub)
|
public bool AnyUnitsAt(CPos a, SubCell sub)
|
||||||
{
|
{
|
||||||
for (var i = influence[a.X, a.Y]; i != null; i = i.Next)
|
for (var i = influence[a]; i != null; i = i.Next)
|
||||||
if (i.SubCell == sub || i.SubCell == SubCell.FullCell)
|
if (i.SubCell == sub || i.SubCell == SubCell.FullCell)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -122,20 +122,25 @@ namespace OpenRA.Traits
|
|||||||
public void AddInfluence(Actor self, IOccupySpace ios)
|
public void AddInfluence(Actor self, IOccupySpace ios)
|
||||||
{
|
{
|
||||||
foreach (var c in ios.OccupiedCells())
|
foreach (var c in ios.OccupiedCells())
|
||||||
influence[c.First.X, c.First.Y] = new InfluenceNode { Next = influence[c.First.X, c.First.Y], SubCell = c.Second, Actor = self };
|
influence[c.First] = new InfluenceNode { Next = influence[c.First], SubCell = c.Second, Actor = self };
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveInfluence(Actor self, IOccupySpace ios)
|
public void RemoveInfluence(Actor self, IOccupySpace ios)
|
||||||
{
|
{
|
||||||
foreach (var c in ios.OccupiedCells())
|
foreach (var c in ios.OccupiedCells())
|
||||||
RemoveInfluenceInner(ref influence[c.First.X, c.First.Y], self);
|
{
|
||||||
|
var temp = influence[c.First];
|
||||||
|
RemoveInfluenceInner(ref temp, self);
|
||||||
|
influence[c.First] = temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveInfluenceInner(ref InfluenceNode influenceNode, Actor toRemove)
|
void RemoveInfluenceInner(ref InfluenceNode influenceNode, Actor toRemove)
|
||||||
{
|
{
|
||||||
if (influenceNode == null)
|
if (influenceNode == null)
|
||||||
return;
|
return;
|
||||||
else if (influenceNode.Actor == toRemove)
|
|
||||||
|
if (influenceNode.Actor == toRemove)
|
||||||
influenceNode = influenceNode.Next;
|
influenceNode = influenceNode.Next;
|
||||||
|
|
||||||
if (influenceNode != null)
|
if (influenceNode != null)
|
||||||
@@ -206,5 +211,11 @@ namespace OpenRA.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Actor> ActorsInWorld()
|
||||||
|
{
|
||||||
|
return actors.SelectMany(a => a.Where(b => b.IsInWorld))
|
||||||
|
.Distinct();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,89 +22,78 @@ namespace OpenRA.Traits
|
|||||||
static readonly CellContents EmptyCell = new CellContents();
|
static readonly CellContents EmptyCell = new CellContents();
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
protected CellContents[,] content;
|
protected CellLayer<CellContents> content;
|
||||||
protected CellContents[,] render;
|
protected CellLayer<CellContents> render;
|
||||||
List<CPos> dirty;
|
List<CPos> dirty;
|
||||||
|
|
||||||
public void Render(WorldRenderer wr)
|
public void Render(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var clip = wr.Viewport.CellBounds;
|
foreach (var cell in wr.Viewport.VisibleCells)
|
||||||
for (var x = clip.Left; x < clip.Right; x++)
|
|
||||||
{
|
{
|
||||||
for (var y = clip.Top; y < clip.Bottom; y++)
|
if (world.ShroudObscures(cell))
|
||||||
{
|
|
||||||
var pos = new CPos(x, y);
|
|
||||||
if (world.ShroudObscures(pos))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var c = render[x, y];
|
var c = render[cell];
|
||||||
if (c.Sprite != null)
|
if (c.Sprite != null)
|
||||||
new SpriteRenderable(c.Sprite, pos.CenterPosition,
|
new SpriteRenderable(c.Sprite, cell.CenterPosition,
|
||||||
WVec.Zero, -511, c.Type.Palette, 1f, true).Render(wr);
|
WVec.Zero, -511, c.Type.Palette, 1f, true).Render(wr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int GetAdjacentCellsWith(ResourceType t, int i, int j)
|
int GetAdjacentCellsWith(ResourceType t, CPos cell)
|
||||||
{
|
{
|
||||||
var sum = 0;
|
var sum = 0;
|
||||||
for (var u = -1; u < 2; u++)
|
for (var u = -1; u < 2; u++)
|
||||||
for (var v = -1; v < 2; v++)
|
for (var v = -1; v < 2; v++)
|
||||||
if (content[i + u, j + v].Type == t)
|
if (content[cell + new CVec(u, v)].Type == t)
|
||||||
++sum;
|
++sum;
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
public void WorldLoaded(World w, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
this.world = w;
|
this.world = w;
|
||||||
content = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];
|
content = new CellLayer<CellContents>(w.Map);
|
||||||
render = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];
|
render = new CellLayer<CellContents>(w.Map);
|
||||||
dirty = new List<CPos>();
|
dirty = new List<CPos>();
|
||||||
|
|
||||||
var resources = w.WorldActor.TraitsImplementing<ResourceType>()
|
var resources = w.WorldActor.TraitsImplementing<ResourceType>()
|
||||||
.ToDictionary(r => r.Info.ResourceType, r => r);
|
.ToDictionary(r => r.Info.ResourceType, r => r);
|
||||||
|
|
||||||
var map = w.Map;
|
foreach (var cell in w.Map.Cells)
|
||||||
for (var x = map.Bounds.Left; x < map.Bounds.Right; x++)
|
|
||||||
{
|
{
|
||||||
for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
|
|
||||||
{
|
|
||||||
var cell = new CPos(x, y);
|
|
||||||
ResourceType t;
|
ResourceType t;
|
||||||
if (!resources.TryGetValue(w.Map.MapResources.Value[x, y].Type, out t))
|
if (!resources.TryGetValue(w.Map.MapResources.Value[cell].Type, out t))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!AllowResourceAt(t, cell))
|
if (!AllowResourceAt(t, cell))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
content[x, y] = CreateResourceCell(t, cell);
|
content[cell] = CreateResourceCell(t, cell);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set initial density based on the number of neighboring resources
|
// Set initial density based on the number of neighboring resources
|
||||||
for (var x = map.Bounds.Left; x < map.Bounds.Right; x++)
|
foreach (var cell in w.Map.Cells)
|
||||||
{
|
{
|
||||||
for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
|
var type = content[cell].Type;
|
||||||
{
|
|
||||||
var type = content[x, y].Type;
|
|
||||||
if (type != null)
|
if (type != null)
|
||||||
{
|
{
|
||||||
// Adjacent includes the current cell, so is always >= 1
|
// Adjacent includes the current cell, so is always >= 1
|
||||||
var adjacent = GetAdjacentCellsWith(type, x, y);
|
var adjacent = GetAdjacentCellsWith(type, cell);
|
||||||
var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9);
|
var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9);
|
||||||
content[x, y].Density = Math.Max(density, 1);
|
var temp = content[cell];
|
||||||
|
temp.Density = Math.Max(density, 1);
|
||||||
|
|
||||||
render[x, y] = content[x, y];
|
render[cell] = content[cell] = temp;
|
||||||
UpdateRenderedSprite(new CPos(x, y));
|
UpdateRenderedSprite(cell);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateRenderedSprite(CPos p)
|
protected virtual void UpdateRenderedSprite(CPos cell)
|
||||||
{
|
{
|
||||||
var t = render[p.X, p.Y];
|
var t = render[cell];
|
||||||
if (t.Density > 0)
|
if (t.Density > 0)
|
||||||
{
|
{
|
||||||
var sprites = t.Type.Variants[t.Variant];
|
var sprites = t.Type.Variants[t.Variant];
|
||||||
@@ -114,7 +103,7 @@ namespace OpenRA.Traits
|
|||||||
else
|
else
|
||||||
t.Sprite = null;
|
t.Sprite = null;
|
||||||
|
|
||||||
render[p.X, p.Y] = t;
|
render[cell] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string ChooseRandomVariant(ResourceType t)
|
protected virtual string ChooseRandomVariant(ResourceType t)
|
||||||
@@ -129,7 +118,7 @@ namespace OpenRA.Traits
|
|||||||
{
|
{
|
||||||
if (!self.World.FogObscures(c))
|
if (!self.World.FogObscures(c))
|
||||||
{
|
{
|
||||||
render[c.X, c.Y] = content[c.X, c.Y];
|
render[c] = content[c];
|
||||||
UpdateRenderedSprite(c);
|
UpdateRenderedSprite(c);
|
||||||
remove.Add(c);
|
remove.Add(c);
|
||||||
}
|
}
|
||||||
@@ -139,15 +128,15 @@ namespace OpenRA.Traits
|
|||||||
dirty.Remove(r);
|
dirty.Remove(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowResourceAt(ResourceType rt, CPos a)
|
public bool AllowResourceAt(ResourceType rt, CPos cell)
|
||||||
{
|
{
|
||||||
if (!world.Map.IsInMap(a.X, a.Y))
|
if (!world.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!rt.Info.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type))
|
if (!rt.Info.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(a))
|
if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -160,9 +149,10 @@ namespace OpenRA.Traits
|
|||||||
|| (currentResourceType == null && AllowResourceAt(newResourceType, cell));
|
|| (currentResourceType == null && AllowResourceAt(newResourceType, cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
CellContents CreateResourceCell(ResourceType t, CPos p)
|
CellContents CreateResourceCell(ResourceType t, CPos cell)
|
||||||
{
|
{
|
||||||
world.Map.CustomTerrain[p.X, p.Y] = world.TileSet.GetTerrainIndex(t.Info.TerrainType);
|
world.Map.CustomTerrain[cell] = world.TileSet.GetTerrainIndex(t.Info.TerrainType);
|
||||||
|
|
||||||
return new CellContents
|
return new CellContents
|
||||||
{
|
{
|
||||||
Type = t,
|
Type = t,
|
||||||
@@ -172,7 +162,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public void AddResource(ResourceType t, CPos p, int n)
|
public void AddResource(ResourceType t, CPos p, int n)
|
||||||
{
|
{
|
||||||
var cell = content[p.X, p.Y];
|
var cell = content[p];
|
||||||
if (cell.Type == null)
|
if (cell.Type == null)
|
||||||
cell = CreateResourceCell(t, p);
|
cell = CreateResourceCell(t, p);
|
||||||
|
|
||||||
@@ -180,58 +170,60 @@ namespace OpenRA.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
cell.Density = Math.Min(cell.Type.Info.MaxDensity, cell.Density + n);
|
cell.Density = Math.Min(cell.Type.Info.MaxDensity, cell.Density + n);
|
||||||
content[p.X, p.Y] = cell;
|
content[p] = cell;
|
||||||
|
|
||||||
if (!dirty.Contains(p))
|
if (!dirty.Contains(p))
|
||||||
dirty.Add(p);
|
dirty.Add(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsFull(CPos c)
|
public bool IsFull(CPos cell)
|
||||||
{
|
{
|
||||||
return content[c.X, c.Y].Density == content[c.X, c.Y].Type.Info.MaxDensity;
|
return content[cell].Density == content[cell].Type.Info.MaxDensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceType Harvest(CPos p)
|
public ResourceType Harvest(CPos cell)
|
||||||
{
|
{
|
||||||
var type = content[p.X, p.Y].Type;
|
var c = content[cell];
|
||||||
if (type == null)
|
if (c.Type == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (--content[p.X, p.Y].Density < 0)
|
if (--c.Density < 0)
|
||||||
{
|
{
|
||||||
content[p.X, p.Y] = EmptyCell;
|
content[cell] = EmptyCell;
|
||||||
world.Map.CustomTerrain[p.X, p.Y] = -1;
|
world.Map.CustomTerrain[cell] = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
content[cell] = c;
|
||||||
|
|
||||||
|
if (!dirty.Contains(cell))
|
||||||
|
dirty.Add(cell);
|
||||||
|
|
||||||
|
return c.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dirty.Contains(p))
|
public void Destroy(CPos cell)
|
||||||
dirty.Add(p);
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy(CPos p)
|
|
||||||
{
|
{
|
||||||
// Don't break other users of CustomTerrain if there are no resources
|
// Don't break other users of CustomTerrain if there are no resources
|
||||||
if (content[p.X, p.Y].Type == null)
|
if (content[cell].Type == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Clear cell
|
// Clear cell
|
||||||
content[p.X, p.Y] = EmptyCell;
|
content[cell] = EmptyCell;
|
||||||
world.Map.CustomTerrain[p.X, p.Y] = -1;
|
world.Map.CustomTerrain[cell] = -1;
|
||||||
|
|
||||||
if (!dirty.Contains(p))
|
if (!dirty.Contains(cell))
|
||||||
dirty.Add(p);
|
dirty.Add(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceType GetResource(CPos p) { return content[p.X, p.Y].Type; }
|
public ResourceType GetResource(CPos cell) { return content[cell].Type; }
|
||||||
public ResourceType GetRenderedResource(CPos p) { return render[p.X, p.Y].Type; }
|
public ResourceType GetRenderedResource(CPos cell) { return render[cell].Type; }
|
||||||
public int GetResourceDensity(CPos p) { return content[p.X, p.Y].Density; }
|
public int GetResourceDensity(CPos cell) { return content[cell].Density; }
|
||||||
public int GetMaxResourceDensity(CPos p)
|
public int GetMaxResourceDensity(CPos cell)
|
||||||
{
|
{
|
||||||
if (content[p.X, p.Y].Type == null)
|
if (content[cell].Type == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return content[p.X, p.Y].Type.Info.MaxDensity;
|
return content[cell].Type.Info.MaxDensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CellContents
|
public struct CellContents
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ namespace OpenRA.Traits
|
|||||||
Actor self;
|
Actor self;
|
||||||
Map map;
|
Map map;
|
||||||
|
|
||||||
int[,] visibleCount;
|
CellLayer<int> visibleCount;
|
||||||
int[,] generatedShroudCount;
|
CellLayer<int> generatedShroudCount;
|
||||||
bool[,] explored;
|
CellLayer<bool> explored;
|
||||||
|
|
||||||
readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;
|
readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;
|
||||||
|
|
||||||
@@ -38,8 +38,6 @@ namespace OpenRA.Traits
|
|||||||
Dictionary<Actor, CPos[]> visibility = new Dictionary<Actor, CPos[]>();
|
Dictionary<Actor, CPos[]> visibility = new Dictionary<Actor, CPos[]>();
|
||||||
Dictionary<Actor, CPos[]> generation = new Dictionary<Actor, CPos[]>();
|
Dictionary<Actor, CPos[]> generation = new Dictionary<Actor, CPos[]>();
|
||||||
|
|
||||||
public Rectangle ExploredBounds { get; private set; }
|
|
||||||
|
|
||||||
public int Hash { get; private set; }
|
public int Hash { get; private set; }
|
||||||
|
|
||||||
public Shroud(Actor self)
|
public Shroud(Actor self)
|
||||||
@@ -47,9 +45,9 @@ namespace OpenRA.Traits
|
|||||||
this.self = self;
|
this.self = self;
|
||||||
map = self.World.Map;
|
map = self.World.Map;
|
||||||
|
|
||||||
visibleCount = new int[map.MapSize.X, map.MapSize.Y];
|
visibleCount = new CellLayer<int>(map);
|
||||||
generatedShroudCount = new int[map.MapSize.X, map.MapSize.Y];
|
generatedShroudCount = new CellLayer<int>(map);
|
||||||
explored = new bool[map.MapSize.X, map.MapSize.Y];
|
explored = new CellLayer<bool>(map);
|
||||||
|
|
||||||
self.World.ActorAdded += AddVisibility;
|
self.World.ActorAdded += AddVisibility;
|
||||||
self.World.ActorRemoved += RemoveVisibility;
|
self.World.ActorRemoved += RemoveVisibility;
|
||||||
@@ -57,9 +55,6 @@ namespace OpenRA.Traits
|
|||||||
self.World.ActorAdded += AddShroudGeneration;
|
self.World.ActorAdded += AddShroudGeneration;
|
||||||
self.World.ActorRemoved += RemoveShroudGeneration;
|
self.World.ActorRemoved += RemoveShroudGeneration;
|
||||||
|
|
||||||
if (!self.World.LobbyInfo.GlobalSettings.Shroud)
|
|
||||||
ExploredBounds = map.Bounds;
|
|
||||||
|
|
||||||
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
|
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,15 +66,12 @@ namespace OpenRA.Traits
|
|||||||
static IEnumerable<CPos> FindVisibleTiles(World world, CPos position, WRange radius)
|
static IEnumerable<CPos> FindVisibleTiles(World world, CPos position, WRange radius)
|
||||||
{
|
{
|
||||||
var r = (radius.Range + 1023) / 1024;
|
var r = (radius.Range + 1023) / 1024;
|
||||||
var min = (position - new CVec(r, r)).Clamp(world.Map.Bounds);
|
var limit = radius.Range * radius.Range;
|
||||||
var max = (position + new CVec(r, r)).Clamp(world.Map.Bounds);
|
|
||||||
|
|
||||||
var circleArea = radius.Range * radius.Range;
|
|
||||||
var pos = position.CenterPosition;
|
var pos = position.CenterPosition;
|
||||||
for (var j = min.Y; j <= max.Y; j++)
|
|
||||||
for (var i = min.X; i <= max.X; i++)
|
foreach (var cell in world.Map.FindTilesInCircle(position, r))
|
||||||
if (circleArea >= (new CPos(i, j).CenterPosition - pos).LengthSquared)
|
if ((cell.CenterPosition - pos).HorizontalLengthSquared <= limit)
|
||||||
yield return new CPos(i, j);
|
yield return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddVisibility(Actor a)
|
void AddVisibility(Actor a)
|
||||||
@@ -92,20 +84,11 @@ namespace OpenRA.Traits
|
|||||||
var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range))
|
var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range))
|
||||||
.Distinct().ToArray();
|
.Distinct().ToArray();
|
||||||
|
|
||||||
// Update bounding rect
|
|
||||||
var r = (rs.Range.Range + 1023) / 1024;
|
|
||||||
|
|
||||||
foreach (var o in origins)
|
|
||||||
{
|
|
||||||
var box = new Rectangle(o.X - r, o.Y - r, 2 * r + 1, 2 * r + 1);
|
|
||||||
ExploredBounds = Rectangle.Union(ExploredBounds, box);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update visibility
|
// Update visibility
|
||||||
foreach (var c in visible)
|
foreach (var c in visible)
|
||||||
{
|
{
|
||||||
visibleCount[c.X, c.Y]++;
|
visibleCount[c]++;
|
||||||
explored[c.X, c.Y] = true;
|
explored[c] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visibility.ContainsKey(a))
|
if (visibility.ContainsKey(a))
|
||||||
@@ -122,7 +105,7 @@ namespace OpenRA.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var c in visible)
|
foreach (var c in visible)
|
||||||
visibleCount[c.X, c.Y]--;
|
visibleCount[c]--;
|
||||||
|
|
||||||
visibility.Remove(a);
|
visibility.Remove(a);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
@@ -147,7 +130,7 @@ namespace OpenRA.Traits
|
|||||||
var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range))
|
var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range))
|
||||||
.Distinct().ToArray();
|
.Distinct().ToArray();
|
||||||
foreach (var c in shrouded)
|
foreach (var c in shrouded)
|
||||||
generatedShroudCount[c.X, c.Y]++;
|
generatedShroudCount[c]++;
|
||||||
|
|
||||||
if (generation.ContainsKey(a))
|
if (generation.ContainsKey(a))
|
||||||
throw new InvalidOperationException("Attempting to add duplicate shroud generation");
|
throw new InvalidOperationException("Attempting to add duplicate shroud generation");
|
||||||
@@ -163,7 +146,7 @@ namespace OpenRA.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var c in shrouded)
|
foreach (var c in shrouded)
|
||||||
generatedShroudCount[c.X, c.Y]--;
|
generatedShroudCount[c]--;
|
||||||
|
|
||||||
generation.Remove(a);
|
generation.Remove(a);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
@@ -203,55 +186,44 @@ namespace OpenRA.Traits
|
|||||||
public void Explore(World world, CPos center, WRange range)
|
public void Explore(World world, CPos center, WRange range)
|
||||||
{
|
{
|
||||||
foreach (var q in FindVisibleTiles(world, center, range))
|
foreach (var q in FindVisibleTiles(world, center, range))
|
||||||
explored[q.X, q.Y] = true;
|
explored[q] = true;
|
||||||
|
|
||||||
var r = (range.Range + 1023) / 1024;
|
|
||||||
var box = new Rectangle(center.X - r, center.Y - r, 2 * r + 1, 2 * r + 1);
|
|
||||||
ExploredBounds = Rectangle.Union(ExploredBounds, box);
|
|
||||||
|
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Explore(Shroud s)
|
public void Explore(Shroud s)
|
||||||
{
|
{
|
||||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
foreach (var cell in map.Cells)
|
||||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
if (s.explored[cell])
|
||||||
if (s.explored[i,j] == true)
|
explored[cell] = true;
|
||||||
explored[i, j] = true;
|
|
||||||
|
|
||||||
ExploredBounds = Rectangle.Union(ExploredBounds, s.ExploredBounds);
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExploreAll(World world)
|
public void ExploreAll(World world)
|
||||||
{
|
{
|
||||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
explored.Clear(true);
|
||||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
|
||||||
explored[i, j] = true;
|
|
||||||
|
|
||||||
ExploredBounds = world.Map.Bounds;
|
|
||||||
|
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetExploration()
|
public void ResetExploration()
|
||||||
{
|
{
|
||||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
foreach (var cell in map.Cells)
|
||||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
explored[cell] = visibleCount[cell] > 0;
|
||||||
explored[i, j] = visibleCount[i, j] > 0;
|
|
||||||
|
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExplored(CPos xy) { return IsExplored(xy.X, xy.Y); }
|
public bool IsExplored(CPos cell)
|
||||||
public bool IsExplored(int x, int y)
|
|
||||||
{
|
{
|
||||||
if (!map.IsInMap(x, y))
|
if (!map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Shroud)
|
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Shroud)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return explored[x, y] && (generatedShroudCount[x, y] == 0 || visibleCount[x, y] > 0);
|
return explored[cell] && (generatedShroudCount[cell] == 0 || visibleCount[cell] > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExplored(Actor a)
|
public bool IsExplored(Actor a)
|
||||||
@@ -259,16 +231,15 @@ namespace OpenRA.Traits
|
|||||||
return GetVisOrigins(a).Any(o => IsExplored(o));
|
return GetVisOrigins(a).Any(o => IsExplored(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible(CPos xy) { return IsVisible(xy.X, xy.Y); }
|
public bool IsVisible(CPos cell)
|
||||||
public bool IsVisible(int x, int y)
|
|
||||||
{
|
{
|
||||||
if (!map.IsInMap(x, y))
|
if (!map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog)
|
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return visibleCount[x, y] > 0;
|
return visibleCount[cell] > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actors are hidden under shroud, but not under fog by default
|
// Actors are hidden under shroud, but not under fog by default
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ namespace OpenRA.Widgets
|
|||||||
{
|
{
|
||||||
TooltipType = WorldTooltipType.None;
|
TooltipType = WorldTooltipType.None;
|
||||||
var cell = worldRenderer.Position(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos)).ToCPos();
|
var cell = worldRenderer.Position(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos)).ToCPos();
|
||||||
if (!world.Map.IsInMap(cell))
|
if (!world.Map.Contains(cell))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (world.ShroudObscures(cell))
|
if (world.ShroudObscures(cell))
|
||||||
|
|||||||
@@ -209,18 +209,20 @@ namespace OpenRA.Widgets
|
|||||||
}
|
}
|
||||||
else if (key == Game.Settings.Keys.SelectUnitsByTypeKey)
|
else if (key == Game.Settings.Keys.SelectUnitsByTypeKey)
|
||||||
{
|
{
|
||||||
var selectedTypes = World.Selection.Actors.Where(
|
var selectedTypes = World.Selection.Actors
|
||||||
x => x.Owner == World.RenderPlayer).Select(a => a.Info);
|
.Where(x => x.Owner == World.RenderPlayer)
|
||||||
|
.Select(a => a.Info);
|
||||||
|
|
||||||
Func<Actor, bool> cond = a => a.Owner == World.RenderPlayer && selectedTypes.Contains(a.Info);
|
Func<Actor, bool> cond = a => a.Owner == World.RenderPlayer && selectedTypes.Contains(a.Info);
|
||||||
var newSelection = SelectActorsInBox(
|
var tl = worldRenderer.Viewport.TopLeft;
|
||||||
World, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight, cond);
|
var br = worldRenderer.Viewport.BottomRight;
|
||||||
|
var newSelection = SelectActorsInBox(World, tl, br, cond);
|
||||||
|
|
||||||
if (newSelection.Count() > selectedTypes.Count())
|
if (newSelection.Count() > selectedTypes.Count())
|
||||||
Game.Debug("Selected across screen");
|
Game.Debug("Selected across screen");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newSelection = World.ActorMap.ActorsInBox(
|
newSelection = World.ActorMap.ActorsInWorld().Where(cond);
|
||||||
World.Map.Bounds.TopLeftAsCPos().TopLeft,
|
|
||||||
World.Map.Bounds.BottomRightAsCPos().BottomRight).Where(cond);
|
|
||||||
Game.Debug("Selected across map");
|
Game.Debug("Selected across map");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,17 +55,6 @@ namespace OpenRA
|
|||||||
public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); }
|
public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); }
|
||||||
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
|
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
|
||||||
|
|
||||||
public Rectangle? VisibleBounds
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (RenderPlayer == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return RenderPlayer.Shroud.ExploredBounds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsReplay
|
public bool IsReplay
|
||||||
{
|
{
|
||||||
get { return orderManager.Connection is ReplayConnection; }
|
get { return orderManager.Connection is ReplayConnection; }
|
||||||
|
|||||||
@@ -97,28 +97,28 @@ namespace OpenRA.Traits
|
|||||||
ClearSides FindClearSides(ResourceType t, CPos p)
|
ClearSides FindClearSides(ResourceType t, CPos p)
|
||||||
{
|
{
|
||||||
var ret = ClearSides.None;
|
var ret = ClearSides.None;
|
||||||
if (render[p.X, p.Y - 1].Type != t)
|
if (render[p + new CVec(0, -1)].Type != t)
|
||||||
ret |= ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight;
|
ret |= ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight;
|
||||||
|
|
||||||
if (render[p.X - 1, p.Y].Type != t)
|
if (render[p + new CVec(-1, 0)].Type != t)
|
||||||
ret |= ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft;
|
ret |= ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft;
|
||||||
|
|
||||||
if (render[p.X + 1, p.Y].Type != t)
|
if (render[p + new CVec(1, 0)].Type != t)
|
||||||
ret |= ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight;
|
ret |= ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight;
|
||||||
|
|
||||||
if (render[p.X, p.Y + 1].Type != t)
|
if (render[p + new CVec(0, 1)].Type != t)
|
||||||
ret |= ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight;
|
ret |= ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight;
|
||||||
|
|
||||||
if (render[p.X - 1, p.Y - 1].Type != t)
|
if (render[p + new CVec(-1, -1)].Type != t)
|
||||||
ret |= ClearSides.TopLeft;
|
ret |= ClearSides.TopLeft;
|
||||||
|
|
||||||
if (render[p.X + 1, p.Y - 1].Type != t)
|
if (render[p + new CVec(1, -1)].Type != t)
|
||||||
ret |= ClearSides.TopRight;
|
ret |= ClearSides.TopRight;
|
||||||
|
|
||||||
if (render[p.X - 1, p.Y + 1].Type != t)
|
if (render[p + new CVec(-1, 1)].Type != t)
|
||||||
ret |= ClearSides.BottomLeft;
|
ret |= ClearSides.BottomLeft;
|
||||||
|
|
||||||
if (render[p.X + 1, p.Y + 1].Type != t)
|
if (render[p + new CVec(1, 1)].Type != t)
|
||||||
ret |= ClearSides.BottomRight;
|
ret |= ClearSides.BottomRight;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -126,7 +126,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
void UpdateRenderedTileInner(CPos p)
|
void UpdateRenderedTileInner(CPos p)
|
||||||
{
|
{
|
||||||
var t = render[p.X, p.Y];
|
var t = render[p];
|
||||||
if (t.Density > 0)
|
if (t.Density > 0)
|
||||||
{
|
{
|
||||||
var clear = FindClearSides(t.Type, p);
|
var clear = FindClearSides(t.Type, p);
|
||||||
@@ -146,7 +146,7 @@ namespace OpenRA.Traits
|
|||||||
else
|
else
|
||||||
t.Sprite = null;
|
t.Sprite = null;
|
||||||
|
|
||||||
render[p.X, p.Y] = t;
|
render[p] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateRenderedSprite(CPos p)
|
protected override void UpdateRenderedSprite(CPos p)
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
|
|
||||||
var path = pathFinder.FindBidiPath(
|
var path = pathFinder.FindBidiPath(
|
||||||
PathSearch.FromPoints(self.World, mobile.Info, self, searchCells, loc, true),
|
PathSearch.FromPoints(self.World, mobile.Info, self, searchCells, loc, true),
|
||||||
PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).InReverse()
|
PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).Reverse()
|
||||||
);
|
);
|
||||||
|
|
||||||
inner = mobile.MoveTo(() => path);
|
inner = mobile.MoveTo(() => path);
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
|
|
||||||
public bool CanLand(CPos cell)
|
public bool CanLand(CPos cell)
|
||||||
{
|
{
|
||||||
if (!self.World.Map.IsInMap(cell))
|
if (!self.World.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (self.World.ActorMap.AnyUnitsAt(cell))
|
if (self.World.ActorMap.AnyUnitsAt(cell))
|
||||||
@@ -246,7 +246,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
|
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
|
||||||
cursor = self.World.Map.IsInMap(target.CenterPosition.ToCPos()) ? "move" : "move-blocked";
|
cursor = self.World.Map.Contains(target.CenterPosition.ToCPos()) ? "move" : "move-blocked";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
{
|
{
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled || !self.World.Map.IsInMap(self.Location))
|
if (IsCanceled || !self.World.Map.Contains(self.Location))
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
var plane = self.Trait<Plane>();
|
var plane = self.Trait<Plane>();
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
|
bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
|
||||||
{
|
{
|
||||||
if (!self.World.Map.IsInMap(location))
|
if (!self.World.Map.Contains(location))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
|
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
|
||||||
|
|||||||
@@ -91,14 +91,14 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
// Set the initial custom terrain types
|
// Set the initial custom terrain types
|
||||||
foreach (var c in footprint.Keys)
|
foreach (var c in footprint.Keys)
|
||||||
self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c);
|
self.World.Map.CustomTerrain[c] = GetTerrainType(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetTerrainType(CPos cell)
|
int GetTerrainType(CPos cell)
|
||||||
{
|
{
|
||||||
var dx = cell - self.Location;
|
var dx = cell - self.Location;
|
||||||
var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y;
|
var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y;
|
||||||
return self.World.TileSet.GetTerrainIndex(new TileReference<ushort, byte>(template, (byte)index));
|
return self.World.TileSet.GetTerrainIndex(new TerrainTile(template, (byte)index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
||||||
@@ -121,7 +121,7 @@ namespace OpenRA.Mods.RA
|
|||||||
IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template)
|
IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template)
|
||||||
{
|
{
|
||||||
return footprint.Select(c => (IRenderable)(new SpriteRenderable(
|
return footprint.Select(c => (IRenderable)(new SpriteRenderable(
|
||||||
wr.Theater.TileSprite(new TileReference<ushort, byte>(template, c.Value)),
|
wr.Theater.TileSprite(new TerrainTile(template, c.Value)),
|
||||||
c.Key.CenterPosition, WVec.Zero, -512, palette, 1f, true))).ToArray();
|
c.Key.CenterPosition, WVec.Zero, -512, palette, 1f, true))).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
// Update map
|
// Update map
|
||||||
foreach (var c in footprint.Keys)
|
foreach (var c in footprint.Keys)
|
||||||
self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c);
|
self.World.Map.CustomTerrain[c] = GetTerrainType(c);
|
||||||
|
|
||||||
// If this bridge repair operation connects two pathfinding domains,
|
// If this bridge repair operation connects two pathfinding domains,
|
||||||
// update the domain index.
|
// update the domain index.
|
||||||
|
|||||||
@@ -19,95 +19,100 @@ namespace OpenRA.Mods.RA
|
|||||||
class BridgeLayerInfo : ITraitInfo
|
class BridgeLayerInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
[ActorReference]
|
[ActorReference]
|
||||||
public readonly string[] Bridges = {"bridge1", "bridge2"};
|
public readonly string[] Bridges = { "bridge1", "bridge2" };
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new BridgeLayer(init.self, this); }
|
public object Create(ActorInitializer init) { return new BridgeLayer(init.self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class BridgeLayer : IWorldLoaded
|
class BridgeLayer : IWorldLoaded
|
||||||
{
|
{
|
||||||
readonly BridgeLayerInfo Info;
|
readonly BridgeLayerInfo info;
|
||||||
readonly World world;
|
readonly World world;
|
||||||
Dictionary<ushort, Pair<string, float>> BridgeTypes = new Dictionary<ushort, Pair<string,float>>();
|
Dictionary<ushort, Pair<string, float>> bridgeTypes = new Dictionary<ushort, Pair<string, float>>();
|
||||||
Bridge[,] Bridges;
|
CellLayer<Bridge> bridges;
|
||||||
|
|
||||||
public BridgeLayer(Actor self, BridgeLayerInfo Info)
|
public BridgeLayer(Actor self, BridgeLayerInfo info)
|
||||||
{
|
{
|
||||||
this.Info = Info;
|
this.info = info;
|
||||||
this.world = self.World;
|
this.world = self.World;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
public void WorldLoaded(World w, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
Bridges = new Bridge[w.Map.MapSize.X, w.Map.MapSize.Y];
|
bridges = new CellLayer<Bridge>(w.Map);
|
||||||
|
|
||||||
// Build a list of templates that should be overlayed with bridges
|
// Build a list of templates that should be overlayed with bridges
|
||||||
foreach(var bridge in Info.Bridges)
|
foreach (var bridge in info.Bridges)
|
||||||
{
|
{
|
||||||
var bi = w.Map.Rules.Actors[bridge].Traits.Get<BridgeInfo>();
|
var bi = w.Map.Rules.Actors[bridge].Traits.Get<BridgeInfo>();
|
||||||
foreach (var template in bi.Templates)
|
foreach (var template in bi.Templates)
|
||||||
BridgeTypes.Add(template.First, Pair.New(bridge, template.Second));
|
bridgeTypes.Add(template.First, Pair.New(bridge, template.Second));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through the map looking for templates to overlay
|
// Loop through the map looking for templates to overlay
|
||||||
for (var i = w.Map.Bounds.Left; i < w.Map.Bounds.Right; i++)
|
for (var i = w.Map.Bounds.Left; i < w.Map.Bounds.Right; i++)
|
||||||
|
{
|
||||||
for (var j = w.Map.Bounds.Top; j < w.Map.Bounds.Bottom; j++)
|
for (var j = w.Map.Bounds.Top; j < w.Map.Bounds.Bottom; j++)
|
||||||
if (BridgeTypes.Keys.Contains(w.Map.MapTiles.Value[i, j].Type))
|
{
|
||||||
ConvertBridgeToActor(w, i, j);
|
var cell = new CPos(i, j);
|
||||||
|
if (bridgeTypes.ContainsKey(w.Map.MapTiles.Value[cell].Type))
|
||||||
|
ConvertBridgeToActor(w, cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Link adjacent (long)-bridges so that artwork is updated correctly
|
// Link adjacent (long)-bridges so that artwork is updated correctly
|
||||||
foreach (var b in w.Actors.SelectMany(a => a.TraitsImplementing<Bridge>()))
|
foreach (var b in w.Actors.SelectMany(a => a.TraitsImplementing<Bridge>()))
|
||||||
b.LinkNeighbouringBridges(w,this);
|
b.LinkNeighbouringBridges(w, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertBridgeToActor(World w, int i, int j)
|
void ConvertBridgeToActor(World w, CPos cell)
|
||||||
{
|
{
|
||||||
// This cell already has a bridge overlaying it from a previous iteration
|
// This cell already has a bridge overlaying it from a previous iteration
|
||||||
if (Bridges[i,j] != null)
|
if (bridges[cell] != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Correlate the tile "image" aka subtile with its position to find the template origin
|
// Correlate the tile "image" aka subtile with its position to find the template origin
|
||||||
var tile = w.Map.MapTiles.Value[i, j].Type;
|
var tile = w.Map.MapTiles.Value[cell].Type;
|
||||||
var index = w.Map.MapTiles.Value[i, j].Index;
|
var index = w.Map.MapTiles.Value[cell].Index;
|
||||||
var template = w.TileSet.Templates[tile];
|
var template = w.TileSet.Templates[tile];
|
||||||
var ni = i - index % template.Size.X;
|
var ni = cell.X - index % template.Size.X;
|
||||||
var nj = j - index / template.Size.X;
|
var nj = cell.Y - index / template.Size.X;
|
||||||
|
|
||||||
// Create a new actor for this bridge and keep track of which subtiles this bridge includes
|
// Create a new actor for this bridge and keep track of which subtiles this bridge includes
|
||||||
var bridge = w.CreateActor(BridgeTypes[tile].First, new TypeDictionary
|
var bridge = w.CreateActor(bridgeTypes[tile].First, new TypeDictionary
|
||||||
{
|
{
|
||||||
new LocationInit(new CPos(ni, nj)),
|
new LocationInit(new CPos(ni, nj)),
|
||||||
new OwnerInit(w.WorldActor.Owner),
|
new OwnerInit(w.WorldActor.Owner),
|
||||||
new HealthInit(BridgeTypes[tile].Second),
|
new HealthInit(bridgeTypes[tile].Second),
|
||||||
}).Trait<Bridge>();
|
}).Trait<Bridge>();
|
||||||
|
|
||||||
var subTiles = new Dictionary<CPos, byte>();
|
var subTiles = new Dictionary<CPos, byte>();
|
||||||
|
|
||||||
// For each subtile in the template
|
// For each subtile in the template
|
||||||
for (byte ind = 0; ind < template.Size.X*template.Size.Y; ind++)
|
for (byte ind = 0; ind < template.Size.X * template.Size.Y; ind++)
|
||||||
{
|
{
|
||||||
// Where do we expect to find the subtile
|
// Where do we expect to find the subtile
|
||||||
var x = ni + ind % template.Size.X;
|
var subtile = new CPos(ni + ind % template.Size.X, nj + ind / template.Size.X);
|
||||||
var y = nj + ind / template.Size.X;
|
|
||||||
|
|
||||||
// This isn't the bridge you're looking for
|
// This isn't the bridge you're looking for
|
||||||
if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].Type != tile ||
|
if (!w.Map.Contains(subtile) || w.Map.MapTiles.Value[subtile].Type != tile ||
|
||||||
w.Map.MapTiles.Value[x, y].Index != ind)
|
w.Map.MapTiles.Value[subtile].Index != ind)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
subTiles.Add(new CPos(x, y), ind);
|
subTiles.Add(subtile, ind);
|
||||||
Bridges[x,y] = bridge;
|
bridges[subtile] = bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge.Create(tile, subTiles);
|
bridge.Create(tile, subTiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to check for neighbouring bridges
|
// Used to check for neighbouring bridges
|
||||||
public Bridge GetBridge(CPos cell)
|
public Bridge GetBridge(CPos cell)
|
||||||
{
|
{
|
||||||
if (!world.Map.IsInMap(cell))
|
if (!world.Map.Contains(cell))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return Bridges[ cell.X, cell.Y ];
|
return bridges[cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
|
|
||||||
public class BuildingInfluence
|
public class BuildingInfluence
|
||||||
{
|
{
|
||||||
Actor[,] influence;
|
CellLayer<Actor> influence;
|
||||||
Map map;
|
Map map;
|
||||||
|
|
||||||
public BuildingInfluence(World world)
|
public BuildingInfluence(World world)
|
||||||
{
|
{
|
||||||
map = world.Map;
|
map = world.Map;
|
||||||
|
|
||||||
influence = new Actor[map.MapSize.X, map.MapSize.Y];
|
influence = new CellLayer<Actor>(map);
|
||||||
|
|
||||||
world.ActorAdded += a =>
|
world.ActorAdded += a =>
|
||||||
{
|
{
|
||||||
@@ -35,8 +35,8 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b.Info, a.Location))
|
foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b.Info, a.Location))
|
||||||
if (map.IsInMap(u) && influence[u.X, u.Y] == null)
|
if (map.Contains(u) && influence[u] == null)
|
||||||
influence[u.X, u.Y] = a;
|
influence[u] = a;
|
||||||
};
|
};
|
||||||
|
|
||||||
world.ActorRemoved += a =>
|
world.ActorRemoved += a =>
|
||||||
@@ -46,17 +46,17 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b.Info, a.Location))
|
foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b.Info, a.Location))
|
||||||
if (map.IsInMap(u) && influence[u.X, u.Y] == a)
|
if (map.Contains(u) && influence[u] == a)
|
||||||
influence[u.X, u.Y] = null;
|
influence[u] = null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Actor GetBuildingAt(CPos cell)
|
public Actor GetBuildingAt(CPos cell)
|
||||||
{
|
{
|
||||||
if (!map.IsInMap(cell))
|
if (!map.Contains(cell))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return influence[cell.X, cell.Y];
|
return influence[cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Don't place under other buildings or custom terrain
|
// Don't place under other buildings or custom terrain
|
||||||
if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c.X, c.Y] != -1)
|
if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c] != -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var index = Game.CosmeticRandom.Next(template.TilesCount);
|
var index = Game.CosmeticRandom.Next(template.TilesCount);
|
||||||
layer.AddTile(c, new TileReference<ushort, byte>(template.Id, (byte)index));
|
layer.AddTile(c, new TerrainTile(template.Id, (byte)index));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -77,10 +77,10 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Don't place under other buildings or custom terrain
|
// Don't place under other buildings or custom terrain
|
||||||
if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c.X, c.Y] != -1)
|
if (bi.GetBuildingAt(c) != self || map.CustomTerrain[c] != -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
layer.AddTile(c, new TileReference<ushort, byte>(template.Id, (byte)i));
|
layer.AddTile(c, new TerrainTile(template.Id, (byte)i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(a) != null) return false;
|
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(a) != null) return false;
|
||||||
if (world.ActorMap.GetUnitsAt(a).Any(b => b != toIgnore)) return false;
|
if (world.ActorMap.GetUnitsAt(a).Any(b => b != toIgnore)) return false;
|
||||||
|
|
||||||
return world.Map.IsInMap(a) && bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type);
|
return world.Map.Contains(a) && bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CanPlaceBuilding(this World world, string name, BuildingInfo building, CPos topLeft, Actor toIgnore)
|
public static bool CanPlaceBuilding(this World world, string name, BuildingInfo building, CPos topLeft, Actor toIgnore)
|
||||||
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Buildings
|
|||||||
|
|
||||||
var res = world.WorldActor.Trait<ResourceLayer>();
|
var res = world.WorldActor.Trait<ResourceLayer>();
|
||||||
return FootprintUtils.Tiles(world.Map.Rules, name, building, topLeft).All(
|
return FootprintUtils.Tiles(world.Map.Rules, name, building, topLeft).All(
|
||||||
t => world.Map.IsInMap(t.X, t.Y) && res.GetResource(t) == null &&
|
t => world.Map.Contains(t) && res.GetResource(t) == null &&
|
||||||
world.IsCellBuildable(t, building, toIgnore));
|
world.IsCellBuildable(t, building, toIgnore));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA
|
|||||||
var world = firedBy.World;
|
var world = firedBy.World;
|
||||||
var targetTile = pos.ToCPos();
|
var targetTile = pos.ToCPos();
|
||||||
|
|
||||||
if (!world.Map.IsInMap(targetTile))
|
if (!world.Map.Contains(targetTile))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var isWater = pos.Z <= 0 && world.Map.GetTerrainInfo(targetTile).IsWater;
|
var isWater = pos.Z <= 0 && world.Map.GetTerrainInfo(targetTile).IsWater;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
||||||
{
|
{
|
||||||
if (!self.World.Map.IsInMap(cell.X, cell.Y)) return false;
|
if (!self.World.Map.Contains(cell)) return false;
|
||||||
|
|
||||||
var type = self.World.Map.GetTerrainInfo(cell).Type;
|
var type = self.World.Map.GetTerrainInfo(cell).Type;
|
||||||
if (!info.TerrainTypes.Contains(type))
|
if (!info.TerrainTypes.Contains(type))
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ namespace OpenRA.Mods.RA.Effects
|
|||||||
|| (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) // Within range
|
|| (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) // Within range
|
||||||
|| (info.RangeLimit != 0 && ticks > info.RangeLimit) // Ran out of fuel
|
|| (info.RangeLimit != 0 && ticks > info.RangeLimit) // Ran out of fuel
|
||||||
|| (!info.High && world.ActorMap.GetUnitsAt(cell).Any(a => a.HasTrait<IBlocksBullets>())) // Hit a wall
|
|| (!info.High && world.ActorMap.GetUnitsAt(cell).Any(a => a.HasTrait<IBlocksBullets>())) // Hit a wall
|
||||||
|| !world.Map.IsInMap(cell) // This also avoids an IndexOutOfRangeException in GetTerrainInfo below.
|
|| !world.Map.Contains(cell) // This also avoids an IndexOutOfRangeException in GetTerrainInfo below.
|
||||||
|| (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType); // Hit incompatible terrain
|
|| (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType); // Hit incompatible terrain
|
||||||
|
|
||||||
if (shouldExplode)
|
if (shouldExplode)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.RA
|
|||||||
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); }
|
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); }
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
||||||
{
|
{
|
||||||
if (!self.World.Map.IsInMap(cell.X, cell.Y))
|
if (!self.World.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type))
|
if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type))
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ namespace OpenRA.Mods.RA
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var location = target.CenterPosition.ToCPos();
|
var location = target.CenterPosition.ToCPos();
|
||||||
if (!self.World.Map.IsInMap(location))
|
if (!self.World.Map.Contains(location))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
cursor = "ability";
|
cursor = "ability";
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
public int MovementCostForCell(World world, CPos cell)
|
public int MovementCostForCell(World world, CPos cell)
|
||||||
{
|
{
|
||||||
if (!world.Map.IsInMap(cell.X, cell.Y))
|
if (!world.Map.Contains(cell))
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
|
|
||||||
var index = world.Map.GetTerrainIndex(cell);
|
var index = world.Map.GetTerrainIndex(cell);
|
||||||
@@ -580,7 +580,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
if (self.Owner.Shroud.IsExplored(location))
|
if (self.Owner.Shroud.IsExplored(location))
|
||||||
cursor = self.World.Map.GetTerrainInfo(location).CustomCursor ?? cursor;
|
cursor = self.World.Map.GetTerrainInfo(location).CustomCursor ?? cursor;
|
||||||
|
|
||||||
if (!self.World.Map.IsInMap(location) || (self.Owner.Shroud.IsExplored(location) &&
|
if (!self.World.Map.Contains(location) || (self.Owner.Shroud.IsExplored(location) &&
|
||||||
unitType.MovementCostForCell(self.World, location) == int.MaxValue))
|
unitType.MovementCostForCell(self.World, location) == int.MaxValue))
|
||||||
cursor = "move-blocked";
|
cursor = "move-blocked";
|
||||||
|
|
||||||
|
|||||||
56
OpenRA.Mods.RA/Move/PathFinder.cs
Executable file → Normal file
56
OpenRA.Mods.RA/Move/PathFinder.cs
Executable file → Normal file
@@ -12,6 +12,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Support;
|
using OpenRA.Support;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
@@ -68,7 +69,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
var pb = FindBidiPath(
|
var pb = FindBidiPath(
|
||||||
PathSearch.FromPoint(world, mi, self, target, from, true),
|
PathSearch.FromPoint(world, mi, self, target, from, true),
|
||||||
PathSearch.FromPoint(world, mi, self, from, target, true).InReverse()
|
PathSearch.FromPoint(world, mi, self, from, target, true).Reverse()
|
||||||
);
|
);
|
||||||
|
|
||||||
CheckSanePath2(pb, from, target);
|
CheckSanePath2(pb, from, target);
|
||||||
@@ -109,7 +110,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
var path = FindBidiPath(
|
var path = FindBidiPath(
|
||||||
PathSearch.FromPoints(world, mi, self, tilesInRange, src, true),
|
PathSearch.FromPoints(world, mi, self, tilesInRange, src, true),
|
||||||
PathSearch.FromPoint(world, mi, self, src, targetCell, true).InReverse()
|
PathSearch.FromPoint(world, mi, self, src, targetCell, true).Reverse()
|
||||||
);
|
);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
@@ -124,21 +125,19 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
{
|
{
|
||||||
List<CPos> path = null;
|
List<CPos> path = null;
|
||||||
|
|
||||||
while (!search.queue.Empty)
|
while (!search.Queue.Empty)
|
||||||
{
|
{
|
||||||
var p = search.Expand(world);
|
var p = search.Expand(world);
|
||||||
if (search.heuristic(p) == 0)
|
if (search.Heuristic(p) == 0)
|
||||||
{
|
{
|
||||||
path = MakePath(search.cellInfo, p);
|
path = MakePath(search.CellInfo, p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
|
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
|
||||||
if (dbg != null)
|
if (dbg != null)
|
||||||
{
|
dbg.AddLayer(search.Considered.Select(p => new Pair<CPos, int>(p, search.CellInfo[p].MinCost)), search.MaxCost, search.Owner);
|
||||||
dbg.AddLayer(search.considered.Select(p => new Pair<CPos, int>(p, search.cellInfo[p.X, p.Y].MinCost)), search.maxCost, search.owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path != null)
|
if (path != null)
|
||||||
return path;
|
return path;
|
||||||
@@ -149,15 +148,15 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<CPos> MakePath(CellInfo[,] cellInfo, CPos destination)
|
static List<CPos> MakePath(CellLayer<CellInfo> cellInfo, CPos destination)
|
||||||
{
|
{
|
||||||
var ret = new List<CPos>();
|
var ret = new List<CPos>();
|
||||||
var pathNode = destination;
|
var pathNode = destination;
|
||||||
|
|
||||||
while (cellInfo[pathNode.X, pathNode.Y].Path != pathNode)
|
while (cellInfo[pathNode].Path != pathNode)
|
||||||
{
|
{
|
||||||
ret.Add(pathNode);
|
ret.Add(pathNode);
|
||||||
pathNode = cellInfo[pathNode.X, pathNode.Y].Path;
|
pathNode = cellInfo[pathNode].Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Add(pathNode);
|
ret.Add(pathNode);
|
||||||
@@ -165,9 +164,8 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CPos> FindBidiPath( /* searches from both ends toward each other */
|
// Searches from both ends toward each other
|
||||||
PathSearch fromSrc,
|
public List<CPos> FindBidiPath(PathSearch fromSrc, PathSearch fromDest)
|
||||||
PathSearch fromDest)
|
|
||||||
{
|
{
|
||||||
using (new PerfSample("Pathfinder"))
|
using (new PerfSample("Pathfinder"))
|
||||||
{
|
{
|
||||||
@@ -176,13 +174,13 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
{
|
{
|
||||||
List<CPos> path = null;
|
List<CPos> path = null;
|
||||||
|
|
||||||
while (!fromSrc.queue.Empty && !fromDest.queue.Empty)
|
while (!fromSrc.Queue.Empty && !fromDest.Queue.Empty)
|
||||||
{
|
{
|
||||||
/* make some progress on the first search */
|
/* make some progress on the first search */
|
||||||
var p = fromSrc.Expand(world);
|
var p = fromSrc.Expand(world);
|
||||||
|
|
||||||
if (fromDest.cellInfo[p.X, p.Y].Seen &&
|
if (fromDest.CellInfo[p].Seen &&
|
||||||
fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity)
|
fromDest.CellInfo[p].MinCost < float.PositiveInfinity)
|
||||||
{
|
{
|
||||||
path = MakeBidiPath(fromSrc, fromDest, p);
|
path = MakeBidiPath(fromSrc, fromDest, p);
|
||||||
break;
|
break;
|
||||||
@@ -191,8 +189,8 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
/* make some progress on the second search */
|
/* make some progress on the second search */
|
||||||
var q = fromDest.Expand(world);
|
var q = fromDest.Expand(world);
|
||||||
|
|
||||||
if (fromSrc.cellInfo[q.X, q.Y].Seen &&
|
if (fromSrc.CellInfo[q].Seen &&
|
||||||
fromSrc.cellInfo[q.X, q.Y].MinCost < float.PositiveInfinity)
|
fromSrc.CellInfo[q].MinCost < float.PositiveInfinity)
|
||||||
{
|
{
|
||||||
path = MakeBidiPath(fromSrc, fromDest, q);
|
path = MakeBidiPath(fromSrc, fromDest, q);
|
||||||
break;
|
break;
|
||||||
@@ -202,8 +200,8 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
|
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
|
||||||
if (dbg != null)
|
if (dbg != null)
|
||||||
{
|
{
|
||||||
dbg.AddLayer(fromSrc.considered.Select(p => new Pair<CPos, int>(p, fromSrc.cellInfo[p.X, p.Y].MinCost)), fromSrc.maxCost, fromSrc.owner);
|
dbg.AddLayer(fromSrc.Considered.Select(p => new Pair<CPos, int>(p, fromSrc.CellInfo[p].MinCost)), fromSrc.MaxCost, fromSrc.Owner);
|
||||||
dbg.AddLayer(fromDest.considered.Select(p => new Pair<CPos, int>(p, fromDest.cellInfo[p.X, p.Y].MinCost)), fromDest.maxCost, fromDest.owner);
|
dbg.AddLayer(fromDest.Considered.Select(p => new Pair<CPos, int>(p, fromDest.CellInfo[p].MinCost)), fromDest.MaxCost, fromDest.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path != null)
|
if (path != null)
|
||||||
@@ -216,25 +214,25 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
static List<CPos> MakeBidiPath(PathSearch a, PathSearch b, CPos p)
|
static List<CPos> MakeBidiPath(PathSearch a, PathSearch b, CPos p)
|
||||||
{
|
{
|
||||||
var ca = a.cellInfo;
|
var ca = a.CellInfo;
|
||||||
var cb = b.cellInfo;
|
var cb = b.CellInfo;
|
||||||
|
|
||||||
var ret = new List<CPos>();
|
var ret = new List<CPos>();
|
||||||
|
|
||||||
var q = p;
|
var q = p;
|
||||||
while (ca[q.X, q.Y].Path != q)
|
while (ca[q].Path != q)
|
||||||
{
|
{
|
||||||
ret.Add(q);
|
ret.Add(q);
|
||||||
q = ca[q.X, q.Y].Path;
|
q = ca[q].Path;
|
||||||
}
|
}
|
||||||
ret.Add(q);
|
ret.Add(q);
|
||||||
|
|
||||||
ret.Reverse();
|
ret.Reverse();
|
||||||
|
|
||||||
q = p;
|
q = p;
|
||||||
while (cb[q.X, q.Y].Path != q)
|
while (cb[q].Path != q)
|
||||||
{
|
{
|
||||||
q = cb[q.X, q.Y].Path;
|
q = cb[q].Path;
|
||||||
ret.Add(q);
|
ret.Add(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,8 +284,8 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
public struct PathDistance : IComparable<PathDistance>
|
public struct PathDistance : IComparable<PathDistance>
|
||||||
{
|
{
|
||||||
public int EstTotal;
|
public readonly int EstTotal;
|
||||||
public CPos Location;
|
public readonly CPos Location;
|
||||||
|
|
||||||
public PathDistance(int estTotal, CPos location)
|
public PathDistance(int estTotal, CPos location)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#region Copyright & License Information
|
#region Copyright & License Information
|
||||||
/*
|
/*
|
||||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||||
* This file is part of OpenRA, which is free software. It is made
|
* 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
|
* available to you under the terms of the GNU General Public License
|
||||||
* as published by the Free Software Foundation. For more information,
|
* as published by the Free Software Foundation. For more information,
|
||||||
@@ -10,45 +10,97 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Move
|
namespace OpenRA.Mods.RA.Move
|
||||||
{
|
{
|
||||||
public sealed class PathSearch : IDisposable
|
public sealed class PathSearch : IDisposable
|
||||||
{
|
{
|
||||||
World world;
|
public CellLayer<CellInfo> CellInfo;
|
||||||
public CellInfo[,] cellInfo;
|
public PriorityQueue<PathDistance> Queue;
|
||||||
public PriorityQueue<PathDistance> queue;
|
public Func<CPos, int> Heuristic;
|
||||||
public Func<CPos, int> heuristic;
|
public bool CheckForBlocked;
|
||||||
|
public Actor IgnoreBuilding;
|
||||||
|
public bool InReverse;
|
||||||
|
public HashSet<CPos> Considered;
|
||||||
|
public Player Owner { get { return self.Owner; } }
|
||||||
|
public int MaxCost;
|
||||||
|
|
||||||
|
Actor self;
|
||||||
|
MobileInfo mobileInfo;
|
||||||
Func<CPos, int> customCost;
|
Func<CPos, int> customCost;
|
||||||
Func<CPos, bool> customBlock;
|
Func<CPos, bool> customBlock;
|
||||||
public bool checkForBlocked;
|
|
||||||
public Actor ignoreBuilding;
|
|
||||||
public bool inReverse;
|
|
||||||
public HashSet<CPos> considered;
|
|
||||||
public int maxCost;
|
|
||||||
Pair<CVec, int>[] nextDirections;
|
Pair<CVec, int>[] nextDirections;
|
||||||
MobileInfo mobileInfo;
|
int laneBias = 1;
|
||||||
Actor self;
|
|
||||||
public Player owner { get { return self.Owner; } }
|
|
||||||
|
|
||||||
public PathSearch(World world, MobileInfo mobileInfo, Actor self)
|
public PathSearch(World world, MobileInfo mobileInfo, Actor self)
|
||||||
{
|
{
|
||||||
this.world = world;
|
this.self = self;
|
||||||
cellInfo = InitCellInfo();
|
CellInfo = InitCellInfo();
|
||||||
this.mobileInfo = mobileInfo;
|
this.mobileInfo = mobileInfo;
|
||||||
this.self = self;
|
this.self = self;
|
||||||
customCost = null;
|
customCost = null;
|
||||||
queue = new PriorityQueue<PathDistance>();
|
Queue = new PriorityQueue<PathDistance>();
|
||||||
considered = new HashSet<CPos>();
|
Considered = new HashSet<CPos>();
|
||||||
maxCost = 0;
|
MaxCost = 0;
|
||||||
nextDirections = CVec.directions.Select(d => new Pair<CVec, int>(d, 0)).ToArray();
|
nextDirections = CVec.directions.Select(d => new Pair<CVec, int>(d, 0)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathSearch InReverse()
|
public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked)
|
||||||
{
|
{
|
||||||
inReverse = true;
|
var search = new PathSearch(world, mi, self)
|
||||||
|
{
|
||||||
|
CheckForBlocked = checkForBlocked
|
||||||
|
};
|
||||||
|
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked)
|
||||||
|
{
|
||||||
|
var search = new PathSearch(world, mi, self)
|
||||||
|
{
|
||||||
|
Heuristic = DefaultEstimator(target),
|
||||||
|
CheckForBlocked = checkForBlocked
|
||||||
|
};
|
||||||
|
|
||||||
|
search.AddInitialCell(from);
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable<CPos> froms, CPos target, bool checkForBlocked)
|
||||||
|
{
|
||||||
|
var search = new PathSearch(world, mi, self)
|
||||||
|
{
|
||||||
|
Heuristic = DefaultEstimator(target),
|
||||||
|
CheckForBlocked = checkForBlocked
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var sl in froms)
|
||||||
|
search.AddInitialCell(sl);
|
||||||
|
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Func<CPos, int> DefaultEstimator(CPos destination)
|
||||||
|
{
|
||||||
|
return here =>
|
||||||
|
{
|
||||||
|
var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y));
|
||||||
|
var straight = Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y);
|
||||||
|
|
||||||
|
// HACK: this relies on fp and cell-size assumptions.
|
||||||
|
var h = (3400 * diag / 24) + 100 * (straight - (2 * diag));
|
||||||
|
return (int)(h * 1.001);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathSearch Reverse()
|
||||||
|
{
|
||||||
|
InReverse = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,13 +112,13 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
public PathSearch WithIgnoredBuilding(Actor b)
|
public PathSearch WithIgnoredBuilding(Actor b)
|
||||||
{
|
{
|
||||||
ignoreBuilding = b;
|
IgnoreBuilding = b;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathSearch WithHeuristic(Func<CPos, int> h)
|
public PathSearch WithHeuristic(Func<CPos, int> h)
|
||||||
{
|
{
|
||||||
heuristic = h;
|
Heuristic = h;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +130,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
public PathSearch WithoutLaneBias()
|
public PathSearch WithoutLaneBias()
|
||||||
{
|
{
|
||||||
LaneBias = 0;
|
laneBias = 0;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,18 +140,20 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LaneBias = 1;
|
|
||||||
|
|
||||||
public CPos Expand(World world)
|
public CPos Expand(World world)
|
||||||
{
|
{
|
||||||
var p = queue.Pop();
|
var p = Queue.Pop();
|
||||||
while (cellInfo[p.Location.X, p.Location.Y].Seen)
|
while (CellInfo[p.Location].Seen)
|
||||||
if (queue.Empty)
|
{
|
||||||
|
if (Queue.Empty)
|
||||||
return p.Location;
|
return p.Location;
|
||||||
else
|
|
||||||
p = queue.Pop();
|
|
||||||
|
|
||||||
cellInfo[p.Location.X, p.Location.Y].Seen = true;
|
p = Queue.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
var pCell = CellInfo[p.Location];
|
||||||
|
pCell.Seen = true;
|
||||||
|
CellInfo[p.Location] = pCell;
|
||||||
|
|
||||||
var thisCost = mobileInfo.MovementCostForCell(world, p.Location);
|
var thisCost = mobileInfo.MovementCostForCell(world, p.Location);
|
||||||
|
|
||||||
@@ -114,7 +168,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This current cell is ok; check all immediate directions:
|
// This current cell is ok; check all immediate directions:
|
||||||
considered.Add(p.Location);
|
Considered.Add(p.Location);
|
||||||
|
|
||||||
for (var i = 0; i < nextDirections.Length; ++i)
|
for (var i = 0; i < nextDirections.Length; ++i)
|
||||||
{
|
{
|
||||||
@@ -124,9 +178,10 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
var newHere = p.Location + d;
|
var newHere = p.Location + d;
|
||||||
|
|
||||||
// Is this direction flat-out unusable or already seen?
|
// Is this direction flat-out unusable or already seen?
|
||||||
if (!world.Map.IsInMap(newHere.X, newHere.Y))
|
if (!world.Map.Contains(newHere))
|
||||||
continue;
|
continue;
|
||||||
if (cellInfo[newHere.X, newHere.Y].Seen)
|
|
||||||
|
if (CellInfo[newHere].Seen)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Now we may seriously consider this direction using heuristics:
|
// Now we may seriously consider this direction using heuristics:
|
||||||
@@ -135,18 +190,19 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
if (costHere == int.MaxValue)
|
if (costHere == int.MaxValue)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!mobileInfo.CanEnterCell(world, self, newHere, ignoreBuilding, checkForBlocked, false))
|
if (!mobileInfo.CanEnterCell(world, self, newHere, IgnoreBuilding, CheckForBlocked, false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (customBlock != null && customBlock(newHere))
|
if (customBlock != null && customBlock(newHere))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var est = heuristic(newHere);
|
var est = Heuristic(newHere);
|
||||||
if (est == int.MaxValue)
|
if (est == int.MaxValue)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var cellCost = costHere;
|
var cellCost = costHere;
|
||||||
if (d.X * d.Y != 0) cellCost = (cellCost * 34) / 24;
|
if (d.X * d.Y != 0)
|
||||||
|
cellCost = (cellCost * 34) / 24;
|
||||||
|
|
||||||
var userCost = 0;
|
var userCost = 0;
|
||||||
if (customCost != null)
|
if (customCost != null)
|
||||||
@@ -156,105 +212,82 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
}
|
}
|
||||||
|
|
||||||
// directional bonuses for smoother flow!
|
// directional bonuses for smoother flow!
|
||||||
if (LaneBias != 0)
|
if (laneBias != 0)
|
||||||
{
|
{
|
||||||
var ux = (newHere.X + (inReverse ? 1 : 0) & 1);
|
var ux = newHere.X + (InReverse ? 1 : 0) & 1;
|
||||||
var uy = (newHere.Y + (inReverse ? 1 : 0) & 1);
|
var uy = newHere.Y + (InReverse ? 1 : 0) & 1;
|
||||||
|
|
||||||
if (ux == 0 && d.Y < 0) cellCost += LaneBias;
|
if (ux == 0 && d.Y < 0)
|
||||||
else if (ux == 1 && d.Y > 0) cellCost += LaneBias;
|
cellCost += laneBias;
|
||||||
if (uy == 0 && d.X < 0) cellCost += LaneBias;
|
else if (ux == 1 && d.Y > 0)
|
||||||
else if (uy == 1 && d.X > 0) cellCost += LaneBias;
|
cellCost += laneBias;
|
||||||
|
|
||||||
|
if (uy == 0 && d.X < 0)
|
||||||
|
cellCost += laneBias;
|
||||||
|
else if (uy == 1 && d.X > 0)
|
||||||
|
cellCost += laneBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCost = cellInfo[p.Location.X, p.Location.Y].MinCost + cellCost;
|
var newCost = CellInfo[p.Location].MinCost + cellCost;
|
||||||
|
|
||||||
// Cost is even higher; next direction:
|
// Cost is even higher; next direction:
|
||||||
if (newCost > cellInfo[newHere.X, newHere.Y].MinCost)
|
if (newCost > CellInfo[newHere].MinCost)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
cellInfo[newHere.X, newHere.Y].Path = p.Location;
|
var hereCell = CellInfo[newHere];
|
||||||
cellInfo[newHere.X, newHere.Y].MinCost = newCost;
|
hereCell.Path = p.Location;
|
||||||
|
hereCell.MinCost = newCost;
|
||||||
|
CellInfo[newHere] = hereCell;
|
||||||
|
|
||||||
nextDirections[i].Second = newCost + est;
|
nextDirections[i].Second = newCost + est;
|
||||||
queue.Add(new PathDistance(newCost + est, newHere));
|
Queue.Add(new PathDistance(newCost + est, newHere));
|
||||||
|
|
||||||
if (newCost > maxCost) maxCost = newCost;
|
if (newCost > MaxCost)
|
||||||
considered.Add(newHere);
|
MaxCost = newCost;
|
||||||
|
|
||||||
|
Considered.Add(newHere);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort to prefer the cheaper direction:
|
// Sort to prefer the cheaper direction:
|
||||||
//Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second));
|
// Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second));
|
||||||
|
|
||||||
return p.Location;
|
return p.Location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddInitialCell(CPos location)
|
public void AddInitialCell(CPos location)
|
||||||
{
|
{
|
||||||
if (!world.Map.IsInMap(location.X, location.Y))
|
if (!self.World.Map.Contains(location))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cellInfo[location.X, location.Y] = new CellInfo(0, location, false);
|
CellInfo[location] = new CellInfo(0, location, false);
|
||||||
queue.Add(new PathDistance(heuristic(location), location));
|
Queue.Add(new PathDistance(Heuristic(location), location));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked)
|
static readonly Queue<CellLayer<CellInfo>> CellInfoPool = new Queue<CellLayer<CellInfo>>();
|
||||||
|
|
||||||
|
static CellLayer<CellInfo> GetFromPool()
|
||||||
{
|
{
|
||||||
var search = new PathSearch(world, mi, self)
|
lock (CellInfoPool)
|
||||||
{
|
return CellInfoPool.Dequeue();
|
||||||
checkForBlocked = checkForBlocked
|
|
||||||
};
|
|
||||||
return search;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked)
|
static void PutBackIntoPool(CellLayer<CellInfo> ci)
|
||||||
{
|
{
|
||||||
var search = new PathSearch(world, mi, self)
|
lock (CellInfoPool)
|
||||||
{
|
CellInfoPool.Enqueue(ci);
|
||||||
heuristic = DefaultEstimator(target),
|
|
||||||
checkForBlocked = checkForBlocked
|
|
||||||
};
|
|
||||||
|
|
||||||
search.AddInitialCell(from);
|
|
||||||
return search;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable<CPos> froms, CPos target, bool checkForBlocked)
|
CellLayer<CellInfo> InitCellInfo()
|
||||||
{
|
{
|
||||||
var search = new PathSearch(world, mi, self)
|
CellLayer<CellInfo> result = null;
|
||||||
{
|
var mapSize = new Size(self.World.Map.MapSize.X, self.World.Map.MapSize.Y);
|
||||||
heuristic = DefaultEstimator(target),
|
|
||||||
checkForBlocked = checkForBlocked
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var sl in froms)
|
// HACK: Uses a static cache so that double-ended searches (which have two PathSearch instances)
|
||||||
search.AddInitialCell(sl);
|
// can implicitly share data. The PathFinder should allocate the CellInfo array and pass it
|
||||||
|
// explicitly to the things that need to share it.
|
||||||
return search;
|
while (CellInfoPool.Count > 0)
|
||||||
}
|
|
||||||
|
|
||||||
static readonly Queue<CellInfo[,]> cellInfoPool = new Queue<CellInfo[,]>();
|
|
||||||
|
|
||||||
static CellInfo[,] GetFromPool()
|
|
||||||
{
|
|
||||||
lock (cellInfoPool)
|
|
||||||
return cellInfoPool.Dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PutBackIntoPool(CellInfo[,] ci)
|
|
||||||
{
|
|
||||||
lock (cellInfoPool)
|
|
||||||
cellInfoPool.Enqueue(ci);
|
|
||||||
}
|
|
||||||
|
|
||||||
CellInfo[,] InitCellInfo()
|
|
||||||
{
|
|
||||||
CellInfo[,] result = null;
|
|
||||||
while (cellInfoPool.Count > 0)
|
|
||||||
{
|
{
|
||||||
var cellInfo = GetFromPool();
|
var cellInfo = GetFromPool();
|
||||||
if (cellInfo.GetUpperBound(0) != world.Map.MapSize.X - 1 ||
|
if (cellInfo.Size != mapSize)
|
||||||
cellInfo.GetUpperBound(1) != world.Map.MapSize.Y - 1)
|
|
||||||
{
|
{
|
||||||
Log.Write("debug", "Discarding old pooled CellInfo of wrong size.");
|
Log.Write("debug", "Discarding old pooled CellInfo of wrong size.");
|
||||||
continue;
|
continue;
|
||||||
@@ -265,36 +298,24 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
result = new CellInfo[world.Map.MapSize.X, world.Map.MapSize.Y];
|
result = new CellLayer<CellInfo>(self.World.Map);
|
||||||
|
|
||||||
for (var x = 0; x < world.Map.MapSize.X; x++)
|
foreach (var cell in self.World.Map.Cells)
|
||||||
for (var y = 0; y < world.Map.MapSize.Y; y++)
|
result[cell] = new CellInfo(int.MaxValue, cell, false);
|
||||||
result[ x, y ] = new CellInfo( int.MaxValue, new CPos( x, y ), false );
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Func<CPos, int> DefaultEstimator(CPos destination)
|
|
||||||
{
|
|
||||||
return here =>
|
|
||||||
{
|
|
||||||
var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y));
|
|
||||||
var straight = (Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y));
|
|
||||||
var h = (3400 * diag / 24) + 100 * (straight - (2 * diag));
|
|
||||||
h = (int)(h * 1.001);
|
|
||||||
return h;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool disposed;
|
bool disposed;
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (disposed)
|
if (disposed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
disposed = true;
|
disposed = true;
|
||||||
|
|
||||||
PutBackIntoPool(cellInfo);
|
PutBackIntoPool(CellInfo);
|
||||||
cellInfo = null;
|
CellInfo = null;
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var location = target.CenterPosition.ToCPos();
|
var location = target.CenterPosition.ToCPos();
|
||||||
if (self.World.Map.IsInMap(location))
|
if (self.World.Map.Contains(location))
|
||||||
{
|
{
|
||||||
cursor = "ability";
|
cursor = "ability";
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Render
|
|||||||
if (self.CenterPosition.Z > 0 || move.IsMoving)
|
if (self.CenterPosition.Z > 0 || move.IsMoving)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return cargo.CurrentAdjacentCells.Any(c => self.World.Map.IsInMap(c)
|
return cargo.CurrentAdjacentCells.Any(c => self.World.Map.Contains(c)
|
||||||
&& info.OpenTerrainTypes.Contains(self.World.Map.GetTerrainInfo(c).Type));
|
&& info.OpenTerrainTypes.Contains(self.World.Map.GetTerrainInfo(c).Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -35,36 +36,41 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public class ShroudRenderer : IRenderShroud, IWorldLoaded
|
public class ShroudRenderer : IRenderShroud, IWorldLoaded
|
||||||
{
|
{
|
||||||
struct ShroudTile
|
class ShroudTile
|
||||||
{
|
{
|
||||||
public CPos Position;
|
public readonly CPos Position;
|
||||||
public float2 ScreenPosition;
|
public readonly float2 ScreenPosition;
|
||||||
public int Variant;
|
public readonly int Variant;
|
||||||
|
|
||||||
public Sprite Fog;
|
public Sprite Fog;
|
||||||
public Sprite Shroud;
|
public Sprite Shroud;
|
||||||
|
|
||||||
|
public ShroudTile(CPos position, float2 screenPosition, int variant)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
ScreenPosition = screenPosition;
|
||||||
|
Variant = variant;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShroudRendererInfo info;
|
||||||
Sprite[] sprites;
|
Sprite[] sprites;
|
||||||
Sprite unexploredTile;
|
Sprite unexploredTile;
|
||||||
int[] spriteMap;
|
int[] spriteMap;
|
||||||
|
|
||||||
ShroudTile[] tiles;
|
CellLayer<ShroudTile> tiles;
|
||||||
int tileStride, variantStride;
|
int variantStride;
|
||||||
|
|
||||||
int shroudHash;
|
int shroudHash;
|
||||||
PaletteReference fogPalette, shroudPalette;
|
PaletteReference fogPalette, shroudPalette;
|
||||||
Rectangle bounds;
|
Map map;
|
||||||
bool useExtendedIndex;
|
|
||||||
|
|
||||||
public ShroudRenderer(World world, ShroudRendererInfo info)
|
public ShroudRenderer(World world, ShroudRendererInfo info)
|
||||||
{
|
{
|
||||||
var map = world.Map;
|
this.info = info;
|
||||||
bounds = map.Bounds;
|
map = world.Map;
|
||||||
useExtendedIndex = info.UseExtendedIndex;
|
|
||||||
|
|
||||||
tiles = new ShroudTile[map.MapSize.X * map.MapSize.Y];
|
tiles = new CellLayer<ShroudTile>(map);
|
||||||
tileStride = map.MapSize.X;
|
|
||||||
|
|
||||||
// Force update on first render
|
// Force update on first render
|
||||||
shroudHash = -1;
|
shroudHash = -1;
|
||||||
@@ -80,14 +86,10 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mapping of shrouded directions -> sprite index
|
// Mapping of shrouded directions -> sprite index
|
||||||
spriteMap = new int[useExtendedIndex ? 256 : 16];
|
spriteMap = new int[info.UseExtendedIndex ? 256 : 16];
|
||||||
for (var i = 0; i < info.Index.Length; i++)
|
for (var i = 0; i < info.Index.Length; i++)
|
||||||
spriteMap[info.Index[i]] = i;
|
spriteMap[info.Index[i]] = i;
|
||||||
|
|
||||||
// Set individual tile variants to reduce tiling
|
|
||||||
for (var i = 0; i < tiles.Length; i++)
|
|
||||||
tiles[i].Variant = Game.CosmeticRandom.Next(info.Variants.Length);
|
|
||||||
|
|
||||||
// Synthesize unexplored tile if it isn't defined
|
// Synthesize unexplored tile if it isn't defined
|
||||||
if (!info.Index.Contains(0))
|
if (!info.Index.Contains(0))
|
||||||
{
|
{
|
||||||
@@ -102,21 +104,21 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
static int FoggedEdges(Shroud s, CPos p, bool useExtendedIndex)
|
static int FoggedEdges(Shroud s, CPos p, bool useExtendedIndex)
|
||||||
{
|
{
|
||||||
if (!s.IsVisible(p.X, p.Y))
|
if (!s.IsVisible(p))
|
||||||
return 15;
|
return 15;
|
||||||
|
|
||||||
// If a side is shrouded then we also count the corners
|
// If a side is shrouded then we also count the corners
|
||||||
var u = 0;
|
var u = 0;
|
||||||
if (!s.IsVisible(p.X, p.Y - 1)) u |= 0x13;
|
if (!s.IsVisible(p + new CVec(0, -1))) u |= 0x13;
|
||||||
if (!s.IsVisible(p.X + 1, p.Y)) u |= 0x26;
|
if (!s.IsVisible(p + new CVec(1, 0))) u |= 0x26;
|
||||||
if (!s.IsVisible(p.X, p.Y + 1)) u |= 0x4C;
|
if (!s.IsVisible(p + new CVec(0, 1))) u |= 0x4C;
|
||||||
if (!s.IsVisible(p.X - 1, p.Y)) u |= 0x89;
|
if (!s.IsVisible(p + new CVec(-1, 0))) u |= 0x89;
|
||||||
|
|
||||||
var uside = u & 0x0F;
|
var uside = u & 0x0F;
|
||||||
if (!s.IsVisible(p.X - 1, p.Y - 1)) u |= 0x01;
|
if (!s.IsVisible(p + new CVec(-1, -1))) u |= 0x01;
|
||||||
if (!s.IsVisible(p.X + 1, p.Y - 1)) u |= 0x02;
|
if (!s.IsVisible(p + new CVec(1, -1))) u |= 0x02;
|
||||||
if (!s.IsVisible(p.X + 1, p.Y + 1)) u |= 0x04;
|
if (!s.IsVisible(p + new CVec(1, 1))) u |= 0x04;
|
||||||
if (!s.IsVisible(p.X - 1, p.Y + 1)) u |= 0x08;
|
if (!s.IsVisible(p + new CVec(-1, 1))) u |= 0x08;
|
||||||
|
|
||||||
// RA provides a set of frames for tiles with shrouded
|
// RA provides a set of frames for tiles with shrouded
|
||||||
// corners but unshrouded edges. We want to detect this
|
// corners but unshrouded edges. We want to detect this
|
||||||
@@ -129,21 +131,21 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
static int ShroudedEdges(Shroud s, CPos p, bool useExtendedIndex)
|
static int ShroudedEdges(Shroud s, CPos p, bool useExtendedIndex)
|
||||||
{
|
{
|
||||||
if (!s.IsExplored(p.X, p.Y))
|
if (!s.IsExplored(p))
|
||||||
return 15;
|
return 15;
|
||||||
|
|
||||||
// If a side is shrouded then we also count the corners
|
// If a side is shrouded then we also count the corners
|
||||||
var u = 0;
|
var u = 0;
|
||||||
if (!s.IsExplored(p.X, p.Y - 1)) u |= 0x13;
|
if (!s.IsExplored(p + new CVec(0, -1))) u |= 0x13;
|
||||||
if (!s.IsExplored(p.X + 1, p.Y)) u |= 0x26;
|
if (!s.IsExplored(p + new CVec(1, 0))) u |= 0x26;
|
||||||
if (!s.IsExplored(p.X, p.Y + 1)) u |= 0x4C;
|
if (!s.IsExplored(p + new CVec(0, 1))) u |= 0x4C;
|
||||||
if (!s.IsExplored(p.X - 1, p.Y)) u |= 0x89;
|
if (!s.IsExplored(p + new CVec(-1, 0))) u |= 0x89;
|
||||||
|
|
||||||
var uside = u & 0x0F;
|
var uside = u & 0x0F;
|
||||||
if (!s.IsExplored(p.X - 1, p.Y - 1)) u |= 0x01;
|
if (!s.IsExplored(p + new CVec(-1, -1))) u |= 0x01;
|
||||||
if (!s.IsExplored(p.X + 1, p.Y - 1)) u |= 0x02;
|
if (!s.IsExplored(p + new CVec(1, -1))) u |= 0x02;
|
||||||
if (!s.IsExplored(p.X + 1, p.Y + 1)) u |= 0x04;
|
if (!s.IsExplored(p + new CVec(1, 1))) u |= 0x04;
|
||||||
if (!s.IsExplored(p.X - 1, p.Y + 1)) u |= 0x08;
|
if (!s.IsExplored(p + new CVec(-1, 1))) u |= 0x08;
|
||||||
|
|
||||||
// RA provides a set of frames for tiles with shrouded
|
// RA provides a set of frames for tiles with shrouded
|
||||||
// corners but unshrouded edges. We want to detect this
|
// corners but unshrouded edges. We want to detect this
|
||||||
@@ -173,15 +175,12 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
public void WorldLoaded(World w, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
// Cache the tile positions to avoid unnecessary calculations
|
// Initialize tile cache
|
||||||
for (var i = bounds.Left; i < bounds.Right; i++)
|
foreach (var cell in map.Cells)
|
||||||
{
|
{
|
||||||
for (var j = bounds.Top; j < bounds.Bottom; j++)
|
var screen = wr.ScreenPosition(cell.CenterPosition);
|
||||||
{
|
var variant = Game.CosmeticRandom.Next(info.Variants.Length);
|
||||||
var k = j * tileStride + i;
|
tiles[cell] = new ShroudTile(cell, screen, variant);
|
||||||
tiles[k].Position = new CPos(i, j);
|
|
||||||
tiles[k].ScreenPosition = wr.ScreenPosition(tiles[k].Position.CenterPosition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fogPalette = wr.Palette("fog");
|
fogPalette = wr.Palette("fog");
|
||||||
@@ -209,22 +208,25 @@ namespace OpenRA.Mods.RA
|
|||||||
if (shroud == null)
|
if (shroud == null)
|
||||||
{
|
{
|
||||||
// Players with no shroud see the whole map so we only need to set the edges
|
// Players with no shroud see the whole map so we only need to set the edges
|
||||||
for (var k = 0; k < tiles.Length; k++)
|
foreach (var cell in map.Cells)
|
||||||
{
|
{
|
||||||
var shrouded = ObserverShroudedEdges(tiles[k].Position, bounds, useExtendedIndex);
|
var t = tiles[cell];
|
||||||
tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant);
|
var shrouded = ObserverShroudedEdges(t.Position, map.Bounds, info.UseExtendedIndex);
|
||||||
tiles[k].Fog = GetTile(shrouded, tiles[k].Variant);
|
|
||||||
|
t.Shroud = GetTile(shrouded, t.Variant);
|
||||||
|
t.Fog = GetTile(shrouded, t.Variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (var k = 0; k < tiles.Length; k++)
|
foreach (var cell in map.Cells)
|
||||||
{
|
{
|
||||||
var shrouded = ShroudedEdges(shroud, tiles[k].Position, useExtendedIndex);
|
var t = tiles[cell];
|
||||||
var fogged = FoggedEdges(shroud, tiles[k].Position, useExtendedIndex);
|
var shrouded = ShroudedEdges(shroud, t.Position, info.UseExtendedIndex);
|
||||||
|
var fogged = FoggedEdges(shroud, t.Position, info.UseExtendedIndex);
|
||||||
|
|
||||||
tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant);
|
t.Shroud = GetTile(shrouded, t.Variant);
|
||||||
tiles[k].Fog = GetTile(fogged, tiles[k].Variant);
|
t.Fog = GetTile(fogged, t.Variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,27 +235,20 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
Update(shroud);
|
Update(shroud);
|
||||||
|
|
||||||
var clip = wr.Viewport.CellBounds;
|
foreach (var cell in wr.Viewport.VisibleCells)
|
||||||
var width = clip.Width;
|
|
||||||
for (var j = clip.Top; j < clip.Bottom; j++)
|
|
||||||
{
|
{
|
||||||
var start = j * tileStride + clip.Left;
|
var t = tiles[cell];
|
||||||
for (var k = 0; k < width; k++)
|
|
||||||
{
|
|
||||||
var s = tiles[start + k].Shroud;
|
|
||||||
var f = tiles[start + k].Fog;
|
|
||||||
|
|
||||||
if (s != null)
|
if (t.Shroud != null)
|
||||||
{
|
{
|
||||||
var pos = tiles[start + k].ScreenPosition - 0.5f * s.size;
|
var pos = t.ScreenPosition - 0.5f * t.Shroud.size;
|
||||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(s, pos, shroudPalette);
|
Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Shroud, pos, shroudPalette);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f != null)
|
if (t.Fog != null)
|
||||||
{
|
{
|
||||||
var pos = tiles[start + k].ScreenPosition - 0.5f * f.size;
|
var pos = t.ScreenPosition - 0.5f * t.Fog.size;
|
||||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(f, pos, fogPalette);
|
Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Fog, pos, fogPalette);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ namespace OpenRA.Mods.RA
|
|||||||
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
|
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
|
||||||
{
|
{
|
||||||
world.CancelInputMode();
|
world.CancelInputMode();
|
||||||
if (mi.Button == expectedButton && world.Map.IsInMap(xy))
|
if (mi.Button == expectedButton && world.Map.Contains(xy))
|
||||||
yield return new Order(order, manager.self, false) { TargetLocation = xy, SuppressVisualFeedback = true };
|
yield return new Order(order, manager.self, false) { TargetLocation = xy, SuppressVisualFeedback = true };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +258,6 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
||||||
public void RenderAfterWorld(WorldRenderer wr, World world) { }
|
public void RenderAfterWorld(WorldRenderer wr, World world) { }
|
||||||
public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
|
public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ namespace OpenRA.Mods.RA
|
|||||||
dirty = new Dictionary<CPos, Sprite>();
|
dirty = new Dictionary<CPos, Sprite>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTile(CPos cell, TileReference<ushort, byte> tile)
|
public void AddTile(CPos cell, TerrainTile tile)
|
||||||
{
|
{
|
||||||
map.CustomTerrain[cell.X, cell.Y] = tileset.GetTerrainIndex(tile);
|
map.CustomTerrain[cell] = tileset.GetTerrainIndex(tile);
|
||||||
|
|
||||||
// Terrain tiles define their origin at the topleft
|
// Terrain tiles define their origin at the topleft
|
||||||
var s = theater.TileSprite(tile);
|
var s = theater.TileSprite(tile);
|
||||||
@@ -59,12 +59,11 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void Render(WorldRenderer wr)
|
public void Render(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var cliprect = wr.Viewport.CellBounds;
|
|
||||||
var pal = wr.Palette("terrain");
|
var pal = wr.Palette("terrain");
|
||||||
|
|
||||||
foreach (var kv in tiles)
|
foreach (var kv in tiles)
|
||||||
{
|
{
|
||||||
if (!cliprect.Contains(kv.Key.X, kv.Key.Y))
|
if (!wr.Viewport.VisibleCells.Contains(kv.Key))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (wr.world.ShroudObscures(kv.Key))
|
if (wr.world.ShroudObscures(kv.Key))
|
||||||
|
|||||||
@@ -53,17 +53,17 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
class MovementClassDomainIndex
|
class MovementClassDomainIndex
|
||||||
{
|
{
|
||||||
Rectangle bounds;
|
Map map;
|
||||||
|
|
||||||
uint movementClass;
|
uint movementClass;
|
||||||
int[,] domains;
|
CellLayer<int> domains;
|
||||||
Dictionary<int, HashSet<int>> transientConnections;
|
Dictionary<int, HashSet<int>> transientConnections;
|
||||||
|
|
||||||
public MovementClassDomainIndex(World world, uint movementClass)
|
public MovementClassDomainIndex(World world, uint movementClass)
|
||||||
{
|
{
|
||||||
bounds = world.Map.Bounds;
|
map = world.Map;
|
||||||
this.movementClass = movementClass;
|
this.movementClass = movementClass;
|
||||||
domains = new int[(bounds.Width + bounds.X), (bounds.Height + bounds.Y)];
|
domains = new CellLayer<int>(world.Map);
|
||||||
transientConnections = new Dictionary<int, HashSet<int>>();
|
transientConnections = new Dictionary<int, HashSet<int>>();
|
||||||
|
|
||||||
BuildDomains(world);
|
BuildDomains(world);
|
||||||
@@ -71,15 +71,15 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public bool IsPassable(CPos p1, CPos p2)
|
public bool IsPassable(CPos p1, CPos p2)
|
||||||
{
|
{
|
||||||
if (!bounds.Contains(p1.X, p1.Y) || !bounds.Contains(p2.X, p2.Y))
|
if (!map.Contains(p1) || !map.Contains(p2))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (domains[p1.X, p1.Y] == domains[p2.X, p2.Y])
|
if (domains[p1] == domains[p2])
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Even though p1 and p2 are in different domains, it's possible
|
// Even though p1 and p2 are in different domains, it's possible
|
||||||
// that some dynamic terrain (i.e. bridges) may connect them.
|
// that some dynamic terrain (i.e. bridges) may connect them.
|
||||||
return HasConnection(GetDomainOf(p1), GetDomainOf(p2));
|
return HasConnection(domains[p1], domains[p2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateCells(World world, HashSet<CPos> dirtyCells)
|
public void UpdateCells(World world, HashSet<CPos> dirtyCells)
|
||||||
@@ -90,22 +90,21 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
// Select all neighbors inside the map boundries
|
// Select all neighbors inside the map boundries
|
||||||
var neighbors = CVec.directions.Select(d => d + cell)
|
var neighbors = CVec.directions.Select(d => d + cell)
|
||||||
.Where(c => bounds.Contains(c.X, c.Y));
|
.Where(c => map.Contains(c));
|
||||||
|
|
||||||
var found = false;
|
var found = false;
|
||||||
foreach (var neighbor in neighbors)
|
foreach (var n in neighbors)
|
||||||
{
|
{
|
||||||
if (!dirtyCells.Contains(neighbor))
|
if (!dirtyCells.Contains(n))
|
||||||
{
|
{
|
||||||
var neighborDomain = GetDomainOf(neighbor);
|
var neighborDomain = domains[n];
|
||||||
|
if (CanTraverseTile(world, n))
|
||||||
var match = CanTraverseTile(world, neighbor);
|
neighborDomains.Add(neighborDomain);
|
||||||
if (match) neighborDomains.Add(neighborDomain);
|
|
||||||
|
|
||||||
// Set ourselves to the first non-dirty neighbor we find.
|
// Set ourselves to the first non-dirty neighbor we find.
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
SetDomain(cell, neighborDomain);
|
domains[cell] = neighborDomain;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,21 +112,9 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (var c1 in neighborDomains)
|
foreach (var c1 in neighborDomains)
|
||||||
{
|
|
||||||
foreach (var c2 in neighborDomains)
|
foreach (var c2 in neighborDomains)
|
||||||
CreateConnection(c1, c2);
|
CreateConnection(c1, c2);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int GetDomainOf(CPos p)
|
|
||||||
{
|
|
||||||
return domains[p.X, p.Y];
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDomain(CPos p, int domain)
|
|
||||||
{
|
|
||||||
domains[p.X, p.Y] = domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasConnection(int d1, int d2)
|
bool HasConnection(int d1, int d2)
|
||||||
{
|
{
|
||||||
@@ -146,6 +133,7 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
if (neighbor == d2)
|
if (neighbor == d2)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!visited.Contains(neighbor))
|
if (!visited.Contains(neighbor))
|
||||||
toProcess.Push(neighbor);
|
toProcess.Push(neighbor);
|
||||||
}
|
}
|
||||||
@@ -180,7 +168,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
var domain = 1;
|
var domain = 1;
|
||||||
|
|
||||||
var visited = new bool[(bounds.Width + bounds.X), (bounds.Height + bounds.Y)];
|
var visited = new CellLayer<bool>(map);
|
||||||
|
|
||||||
var toProcess = new Queue<CPos>();
|
var toProcess = new Queue<CPos>();
|
||||||
toProcess.Enqueue(new CPos(map.Bounds.Left, map.Bounds.Top));
|
toProcess.Enqueue(new CPos(map.Bounds.Left, map.Bounds.Top));
|
||||||
@@ -192,7 +180,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
// Technically redundant with the check in the inner loop, but prevents
|
// Technically redundant with the check in the inner loop, but prevents
|
||||||
// ballooning the domain counter.
|
// ballooning the domain counter.
|
||||||
if (visited[start.X, start.Y])
|
if (visited[start])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var domainQueue = new Queue<CPos>();
|
var domainQueue = new Queue<CPos>();
|
||||||
@@ -205,7 +193,7 @@ namespace OpenRA.Mods.RA
|
|||||||
while (domainQueue.Count != 0)
|
while (domainQueue.Count != 0)
|
||||||
{
|
{
|
||||||
var n = domainQueue.Dequeue();
|
var n = domainQueue.Dequeue();
|
||||||
if (visited[n.X, n.Y])
|
if (visited[n])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var candidatePassable = CanTraverseTile(world, n);
|
var candidatePassable = CanTraverseTile(world, n);
|
||||||
@@ -215,12 +203,12 @@ namespace OpenRA.Mods.RA
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
visited[n.X, n.Y] = true;
|
visited[n] = true;
|
||||||
SetDomain(n, domain);
|
domains[n] = domain;
|
||||||
|
|
||||||
// Don't crawl off the map, or add already-visited cells
|
// Don't crawl off the map, or add already-visited cells
|
||||||
var neighbors = CVec.directions.Select(d => n + d)
|
var neighbors = CVec.directions.Select(d => n + d)
|
||||||
.Where(p => bounds.Contains(p.X, p.Y) && !visited[p.X, p.Y]);
|
.Where(p => map.Contains(p) && !visited[p]);
|
||||||
|
|
||||||
foreach (var neighbor in neighbors)
|
foreach (var neighbor in neighbors)
|
||||||
domainQueue.Enqueue(neighbor);
|
domainQueue.Enqueue(neighbor);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using OpenRA;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
@@ -20,7 +21,7 @@ namespace OpenRA.Mods.RA
|
|||||||
class PathfinderDebugOverlayInfo : TraitInfo<PathfinderDebugOverlay> { }
|
class PathfinderDebugOverlayInfo : TraitInfo<PathfinderDebugOverlay> { }
|
||||||
class PathfinderDebugOverlay : IRenderOverlay, IWorldLoaded
|
class PathfinderDebugOverlay : IRenderOverlay, IWorldLoaded
|
||||||
{
|
{
|
||||||
Dictionary<Player, int[,]> layers;
|
Dictionary<Player, CellLayer<int>> layers;
|
||||||
int refreshTick;
|
int refreshTick;
|
||||||
World world;
|
World world;
|
||||||
public bool Visible;
|
public bool Visible;
|
||||||
@@ -29,7 +30,7 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
world = w;
|
world = w;
|
||||||
refreshTick = 0;
|
refreshTick = 0;
|
||||||
layers = new Dictionary<Player, int[,]>(8);
|
layers = new Dictionary<Player, CellLayer<int>>(8);
|
||||||
|
|
||||||
// Enabled via Cheats menu
|
// Enabled via Cheats menu
|
||||||
Visible = false;
|
Visible = false;
|
||||||
@@ -39,15 +40,15 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
if (maxWeight == 0) return;
|
if (maxWeight == 0) return;
|
||||||
|
|
||||||
int[,] layer;
|
CellLayer<int> layer;
|
||||||
if (!layers.TryGetValue(pl, out layer))
|
if (!layers.TryGetValue(pl, out layer))
|
||||||
{
|
{
|
||||||
layer = new int[world.Map.MapSize.X, world.Map.MapSize.Y];
|
layer = new CellLayer<int>(world.Map);
|
||||||
layers.Add(pl, layer);
|
layers.Add(pl, layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var p in cellWeights)
|
foreach (var p in cellWeights)
|
||||||
layer[p.First.X, p.First.Y] = Math.Min(128, layer[p.First.X, p.First.Y] + (maxWeight - p.Second) * 64 / maxWeight);
|
layer[p.First] = Math.Min(128, layer[p.First] + (maxWeight - p.Second) * 64 / maxWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Render(WorldRenderer wr)
|
public void Render(WorldRenderer wr)
|
||||||
@@ -59,31 +60,27 @@ namespace OpenRA.Mods.RA
|
|||||||
var doDim = refreshTick - world.WorldTick <= 0;
|
var doDim = refreshTick - world.WorldTick <= 0;
|
||||||
if (doDim) refreshTick = world.WorldTick + 20;
|
if (doDim) refreshTick = world.WorldTick + 20;
|
||||||
|
|
||||||
var viewBounds = wr.Viewport.CellBounds;
|
|
||||||
foreach (var pair in layers)
|
foreach (var pair in layers)
|
||||||
{
|
{
|
||||||
var c = (pair.Key != null) ? pair.Key.Color.RGB : Color.PaleTurquoise;
|
var c = (pair.Key != null) ? pair.Key.Color.RGB : Color.PaleTurquoise;
|
||||||
var layer = pair.Value;
|
var layer = pair.Value;
|
||||||
|
|
||||||
// Only render quads in viewing range:
|
// Only render quads in viewing range:
|
||||||
for (var j = viewBounds.Top; j <= viewBounds.Bottom; ++j)
|
foreach (var cell in wr.Viewport.VisibleCells)
|
||||||
{
|
{
|
||||||
for (var i = viewBounds.Left; i <= viewBounds.Right; ++i)
|
if (layer[cell] <= 0)
|
||||||
{
|
|
||||||
if (layer[i, j] <= 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var w = Math.Max(0, Math.Min(layer[i, j], 128));
|
var w = Math.Max(0, Math.Min(layer[cell], 128));
|
||||||
if (doDim)
|
if (doDim)
|
||||||
layer[i, j] = layer[i, j] * 5 / 6;
|
layer[cell] = layer[cell] * 5 / 6;
|
||||||
|
|
||||||
// TODO: This doesn't make sense for isometric terrain
|
// TODO: This doesn't make sense for isometric terrain
|
||||||
var tl = wr.ScreenPxPosition(new CPos(i, j).TopLeft);
|
var tl = wr.ScreenPxPosition(cell.TopLeft);
|
||||||
var br = wr.ScreenPxPosition(new CPos(i, j).BottomRight);
|
var br = wr.ScreenPxPosition(cell.BottomRight);
|
||||||
qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c));
|
qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,12 +120,11 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void Render(WorldRenderer wr)
|
public void Render(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var cliprect = wr.Viewport.CellBounds;
|
|
||||||
var pal = wr.Palette("terrain");
|
var pal = wr.Palette("terrain");
|
||||||
|
|
||||||
foreach (var kv in tiles)
|
foreach (var kv in tiles)
|
||||||
{
|
{
|
||||||
if (!cliprect.Contains(kv.Key.X, kv.Key.Y))
|
if (!wr.Viewport.VisibleCells.Contains(kv.Key))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (world.ShroudObscures(kv.Key))
|
if (world.ShroudObscures(kv.Key))
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ namespace OpenRA.Utility
|
|||||||
var width = Exts.ParseIntegerInvariant(mapSection.GetValue("Width", "0"));
|
var width = Exts.ParseIntegerInvariant(mapSection.GetValue("Width", "0"));
|
||||||
var height = Exts.ParseIntegerInvariant(mapSection.GetValue("Height", "0"));
|
var height = Exts.ParseIntegerInvariant(mapSection.GetValue("Height", "0"));
|
||||||
mapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64;
|
mapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64;
|
||||||
|
var size = new Size(mapSize, mapSize);
|
||||||
|
|
||||||
map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(iniFile));
|
map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(iniFile));
|
||||||
map.Author = "Westwood Studios";
|
map.Author = "Westwood Studios";
|
||||||
@@ -148,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 TileReference<byte, byte>[mapSize, mapSize]);
|
map.MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(size));
|
||||||
map.MapTiles = Exts.Lazy(() => new TileReference<ushort, byte>[mapSize, mapSize]);
|
map.MapTiles = Exts.Lazy(() => new CellLayer<TerrainTile>(size));
|
||||||
|
|
||||||
map.Options = new MapOptions();
|
map.Options = new MapOptions();
|
||||||
|
|
||||||
@@ -250,20 +251,20 @@ namespace OpenRA.Utility
|
|||||||
|
|
||||||
void UnpackRATileData(MemoryStream ms)
|
void UnpackRATileData(MemoryStream ms)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < mapSize; i++)
|
var types = new ushort[mapSize, mapSize];
|
||||||
for (var j = 0; j < mapSize; j++)
|
|
||||||
map.MapTiles.Value[i, j] = new TileReference<ushort, byte>();
|
|
||||||
|
|
||||||
for (var j = 0; j < mapSize; j++)
|
for (var j = 0; j < mapSize; j++)
|
||||||
|
{
|
||||||
for (var i = 0; i < mapSize; i++)
|
for (var i = 0; i < mapSize; i++)
|
||||||
{
|
{
|
||||||
var tileID = ms.ReadUInt16();
|
var tileID = ms.ReadUInt16();
|
||||||
map.MapTiles.Value[i, j].Type = tileID == (ushort)0 ? (ushort)255 : tileID; // RAED weirdness
|
types[i, j] = tileID == (ushort)0 ? (ushort)255 : tileID; // RAED weirdness
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var j = 0; j < mapSize; j++)
|
for (var j = 0; j < mapSize; j++)
|
||||||
for (var i = 0; i < mapSize; i++)
|
for (var i = 0; i < mapSize; i++)
|
||||||
map.MapTiles.Value[i, j].Index = ms.ReadUInt8();
|
map.MapTiles.Value[new CPos(i, j)] = new TerrainTile(types[i, j], ms.ReadUInt8());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnpackRAOverlayData(MemoryStream ms)
|
void UnpackRAOverlayData(MemoryStream ms)
|
||||||
@@ -278,14 +279,15 @@ namespace OpenRA.Utility
|
|||||||
if (o != 255 && overlayResourceMapping.ContainsKey(redAlertOverlayNames[o]))
|
if (o != 255 && overlayResourceMapping.ContainsKey(redAlertOverlayNames[o]))
|
||||||
res = overlayResourceMapping[redAlertOverlayNames[o]];
|
res = overlayResourceMapping[redAlertOverlayNames[o]];
|
||||||
|
|
||||||
map.MapResources.Value[i, j] = new TileReference<byte, byte>(res.First, res.Second);
|
var cell = new CPos(i, j);
|
||||||
|
map.MapResources.Value[cell] = new ResourceTile(res.First, res.Second);
|
||||||
|
|
||||||
if (o != 255 && overlayActorMapping.ContainsKey(redAlertOverlayNames[o]))
|
if (o != 255 && overlayActorMapping.ContainsKey(redAlertOverlayNames[o]))
|
||||||
{
|
{
|
||||||
map.Actors.Value.Add("Actor" + actorCount++,
|
map.Actors.Value.Add("Actor" + actorCount++,
|
||||||
new ActorReference(overlayActorMapping[redAlertOverlayNames[o]])
|
new ActorReference(overlayActorMapping[redAlertOverlayNames[o]])
|
||||||
{
|
{
|
||||||
new LocationInit(new CPos(i, j)),
|
new LocationInit(cell),
|
||||||
new OwnerInit("Neutral")
|
new OwnerInit("Neutral")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -313,16 +315,13 @@ namespace OpenRA.Utility
|
|||||||
|
|
||||||
void UnpackCncTileData(Stream ms)
|
void UnpackCncTileData(Stream ms)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < mapSize; i++)
|
|
||||||
for (var j = 0; j < mapSize; j++)
|
|
||||||
map.MapTiles.Value[i, j] = new TileReference<ushort, byte>();
|
|
||||||
|
|
||||||
for (var j = 0; j < mapSize; j++)
|
for (var j = 0; j < mapSize; j++)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < mapSize; i++)
|
for (var i = 0; i < mapSize; i++)
|
||||||
{
|
{
|
||||||
map.MapTiles.Value[i, j].Type = ms.ReadUInt8();
|
var type = ms.ReadUInt8();
|
||||||
map.MapTiles.Value[i, j].Index = ms.ReadUInt8();
|
var index = ms.ReadUInt8();
|
||||||
|
map.MapTiles.Value[new CPos(i, j)] = new TerrainTile(type, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +341,7 @@ namespace OpenRA.Utility
|
|||||||
if (overlayResourceMapping.ContainsKey(kv.Value.ToLower()))
|
if (overlayResourceMapping.ContainsKey(kv.Value.ToLower()))
|
||||||
res = overlayResourceMapping[kv.Value.ToLower()];
|
res = overlayResourceMapping[kv.Value.ToLower()];
|
||||||
|
|
||||||
map.MapResources.Value[cell.X, cell.Y] = new TileReference<byte, byte>(res.First, res.Second);
|
map.MapResources.Value[cell] = new ResourceTile(res.First, res.Second);
|
||||||
|
|
||||||
if (overlayActorMapping.ContainsKey(kv.Value.ToLower()))
|
if (overlayActorMapping.ContainsKey(kv.Value.ToLower()))
|
||||||
map.Actors.Value.Add("Actor" + actorCount++,
|
map.Actors.Value.Add("Actor" + actorCount++,
|
||||||
|
|||||||
Reference in New Issue
Block a user