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 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;
|
||||
if (tile[z].Length > 0)
|
||||
surface.Map.MapTiles.Value[u + pos.X, v + pos.Y] =
|
||||
new TileReference<ushort, byte>
|
||||
{
|
||||
Type = brushTemplate.N,
|
||||
Index = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z,
|
||||
};
|
||||
{
|
||||
var 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);
|
||||
if (surface.Chunks.ContainsKey(ch))
|
||||
@@ -70,14 +69,15 @@ namespace OpenRA.Editor
|
||||
void FloodFillWithBrush(Surface s, CPos pos)
|
||||
{
|
||||
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];
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -86,7 +86,7 @@ namespace OpenRA.Editor
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
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;
|
||||
|
||||
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++)
|
||||
{
|
||||
s.Map.MapTiles.Value[x, p.Y] = new TileReference<ushort, byte> { Type = brushTemplate.N, Index = (byte)0 };
|
||||
if (s.Map.MapTiles.Value[x, p.Y - 1].Type == replace.Type)
|
||||
s.Map.MapTiles.Value[new CPos(x, p.Y)] = new TerrainTile(brushTemplate.N, (byte)0);
|
||||
if (s.Map.MapTiles.Value[new CPos(x, p.Y - 1)].Type == replace.Type)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -107,13 +107,13 @@ namespace OpenRA.Editor
|
||||
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 (;;)
|
||||
{
|
||||
var q = p + d;
|
||||
if (!s.Map.IsInMap(q)) return p;
|
||||
if (s.Map.MapTiles.Value[q.X, q.Y].Type != replace.Type) return p;
|
||||
if (!s.Map.Contains(q)) return p;
|
||||
if (s.Map.MapTiles.Value[q].Type != replace.Type) return p;
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,7 +641,8 @@ namespace OpenRA.Editor
|
||||
for (var i = 0; i < surface1.Map.MapSize.X; i++)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -654,9 +655,12 @@ namespace OpenRA.Editor
|
||||
for (var u = -1; u < 2; u++)
|
||||
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;
|
||||
if (surface1.Map.MapResources.Value[x + u, y + v].Type == resourceType)
|
||||
|
||||
if (surface1.Map.MapResources.Value[cell].Type == resourceType)
|
||||
++sum;
|
||||
}
|
||||
|
||||
@@ -666,7 +670,7 @@ namespace OpenRA.Editor
|
||||
int GetResourceValue(int x, int y)
|
||||
{
|
||||
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;
|
||||
if (type == 1)
|
||||
imageLength = 12;
|
||||
|
||||
@@ -21,12 +21,9 @@ namespace OpenRA.Editor
|
||||
|
||||
public void Apply(Surface surface)
|
||||
{
|
||||
surface.Map.MapResources.Value[surface.GetBrushLocation().X, surface.GetBrushLocation().Y]
|
||||
= new TileReference<byte, byte>
|
||||
{
|
||||
Type = (byte)resourceTemplate.Info.ResourceType,
|
||||
Index = (byte)random.Next(resourceTemplate.Info.MaxDensity)
|
||||
};
|
||||
var type = (byte)resourceTemplate.Info.ResourceType;
|
||||
var index = (byte)random.Next(resourceTemplate.Info.MaxDensity);
|
||||
surface.Map.MapResources.Value[surface.GetBrushLocation()] = new ResourceTile(type, index);
|
||||
|
||||
var ch = new int2(surface.GetBrushLocation().X / Surface.ChunkSize,
|
||||
surface.GetBrushLocation().Y / Surface.ChunkSize);
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace OpenRA.Editor
|
||||
public bool ShowRuler;
|
||||
|
||||
public bool IsPaste { get { return TileSelection != null && ResourceSelection != null; } }
|
||||
public TileReference<ushort, byte>[,] TileSelection;
|
||||
public TileReference<byte, byte>[,] ResourceSelection;
|
||||
public TerrainTile[,] TileSelection;
|
||||
public ResourceTile[,] ResourceSelection;
|
||||
public CPos SelectionStart;
|
||||
public CPos SelectionEnd;
|
||||
|
||||
@@ -206,9 +206,9 @@ namespace OpenRA.Editor
|
||||
var key = Map.Actors.Value.FirstOrDefault(a => a.Value.Location() == brushLocation);
|
||||
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);
|
||||
if (Chunks.ContainsKey(ch))
|
||||
{
|
||||
@@ -271,7 +271,8 @@ namespace OpenRA.Editor
|
||||
for (var i = 0; i < ChunkSize; i++)
|
||||
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 index = (tr.Index < tile.Count) ? tr.Index : (byte)0;
|
||||
var rawImage = tile[index];
|
||||
@@ -279,9 +280,9 @@ namespace OpenRA.Editor
|
||||
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();
|
||||
|
||||
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(),
|
||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
@@ -508,16 +509,17 @@ namespace OpenRA.Editor
|
||||
var width = Math.Abs((start - end).X);
|
||||
var height = Math.Abs((start - end).Y);
|
||||
|
||||
TileSelection = new TileReference<ushort, byte>[width, height];
|
||||
ResourceSelection = new TileReference<byte, byte>[width, height];
|
||||
TileSelection = new TerrainTile[width, height];
|
||||
ResourceSelection = new ResourceTile[width, height];
|
||||
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
// TODO: crash prevention
|
||||
TileSelection[x, y] = Map.MapTiles.Value[start.X + x, start.Y + y];
|
||||
ResourceSelection[x, y] = Map.MapResources.Value[start.X + x, start.Y + y];
|
||||
var cell = new CPos(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 mapY = loc.Y + y;
|
||||
var cell = new CPos(mapX, mapY);
|
||||
|
||||
// TODO: crash prevention for outside of bounds
|
||||
Map.MapTiles.Value[mapX, mapY] = TileSelection[x, y];
|
||||
Map.MapResources.Value[mapX, mapY] = ResourceSelection[x, y];
|
||||
Map.MapTiles.Value[cell] = TileSelection[x, y];
|
||||
Map.MapResources.Value[cell] = ResourceSelection[x, y];
|
||||
|
||||
var ch = new int2(mapX / ChunkSize, mapY / ChunkSize);
|
||||
if (Chunks.ContainsKey(ch))
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace OpenRA.GameRules
|
||||
if (target.Type == TargetType.Terrain)
|
||||
{
|
||||
var cell = target.CenterPosition.ToCPos();
|
||||
if (!world.Map.IsInMap(cell))
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
|
||||
var cellInfo = world.Map.GetTerrainInfo(cell);
|
||||
|
||||
@@ -74,8 +74,9 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
|
||||
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();
|
||||
|
||||
if (res == null)
|
||||
continue;
|
||||
|
||||
@@ -135,7 +136,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
var color = t.Trait.RadarSignatureColor(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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,12 @@ namespace OpenRA.Graphics
|
||||
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
|
||||
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[i, j]);
|
||||
var pos = wr.ScreenPosition(new CPos(i, j).CenterPosition) - 0.5f * tile.size;
|
||||
Util.FastCreateQuad(vertices, pos, tile, terrainPalette, nv, tile.size);
|
||||
nv += 4;
|
||||
}
|
||||
var tile = wr.Theater.TileSprite(map.MapTiles.Value[cell]);
|
||||
var pos = wr.ScreenPosition(cell.CenterPosition) - 0.5f * tile.size;
|
||||
Util.FastCreateQuad(vertices, pos, tile, terrainPalette, nv, tile.size);
|
||||
nv += 4;
|
||||
}
|
||||
|
||||
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
|
||||
@@ -46,9 +43,9 @@ namespace OpenRA.Graphics
|
||||
public void Draw(WorldRenderer wr, Viewport viewport)
|
||||
{
|
||||
var verticesPerRow = 4*map.Bounds.Width;
|
||||
var bounds = viewport.CellBounds;
|
||||
var firstRow = bounds.Top - map.Bounds.Top;
|
||||
var lastRow = bounds.Bottom - map.Bounds.Top;
|
||||
var cells = viewport.VisibleCells;
|
||||
var firstRow = cells.TopLeft.Y - map.Bounds.Top;
|
||||
var lastRow = cells.BottomRight.Y - map.Bounds.Top + 1;
|
||||
|
||||
if (lastRow < 0 || firstRow > map.Bounds.Height)
|
||||
return;
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace OpenRA.Graphics
|
||||
missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1));
|
||||
}
|
||||
|
||||
public Sprite TileSprite(TileReference<ushort, byte> r)
|
||||
public Sprite TileSprite(TerrainTile r)
|
||||
{
|
||||
Sprite[] template;
|
||||
if (!templates.TryGetValue(r.Type, out template))
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
@@ -46,7 +47,8 @@ namespace OpenRA.Graphics
|
||||
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
|
||||
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
|
||||
int2 viewportSize;
|
||||
bool cellBoundsDirty = true;
|
||||
CellRegion cells;
|
||||
bool cellsDirty = true;
|
||||
|
||||
float zoom = 1f;
|
||||
public float Zoom
|
||||
@@ -60,7 +62,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
zoom = value;
|
||||
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)
|
||||
{
|
||||
CenterLocation = worldRenderer.ScreenPxPosition(pos).Clamp(mapBounds);
|
||||
cellBoundsDirty = true;
|
||||
cellsDirty = true;
|
||||
}
|
||||
|
||||
public void Scroll(float2 delta, bool ignoreBorders)
|
||||
{
|
||||
// Convert scroll delta from world-px to viewport-px
|
||||
CenterLocation += (1f / Zoom * delta).ToInt2();
|
||||
cellBoundsDirty = true;
|
||||
cellsDirty = true;
|
||||
|
||||
if (!ignoreBorders)
|
||||
CenterLocation = CenterLocation.Clamp(mapBounds);
|
||||
@@ -129,33 +131,30 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
get
|
||||
{
|
||||
var r = CellBounds;
|
||||
var ctl = new CPos(r.Left, r.Top).TopLeft;
|
||||
var cbr = new CPos(r.Right, r.Bottom).TopLeft;
|
||||
var ctl = VisibleCells.TopLeft.TopLeft;
|
||||
var cbr = VisibleCells.BottomRight.BottomRight;
|
||||
var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl)).Clamp(ScreenClip);
|
||||
var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr)).Clamp(ScreenClip);
|
||||
return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle (in cell coords) of cells that are currently visible on the screen
|
||||
Rectangle cachedRect;
|
||||
public Rectangle CellBounds
|
||||
public CellRegion VisibleCells
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cellBoundsDirty)
|
||||
if (cellsDirty)
|
||||
{
|
||||
var boundary = new CVec(1, 1);
|
||||
var tl = worldRenderer.Position(TopLeft).ToCPos() - boundary;
|
||||
var br = worldRenderer.Position(BottomRight).ToCPos() + boundary;
|
||||
// Calculate the intersection of the visible rectangle and the map.
|
||||
var map = worldRenderer.world.Map;
|
||||
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);
|
||||
cellBoundsDirty = false;
|
||||
cells = new CellRegion(tl, br);
|
||||
cellsDirty = false;
|
||||
}
|
||||
|
||||
var b = worldRenderer.world.VisibleBounds;
|
||||
return b.HasValue ? Rectangle.Intersect(cachedRect, b.Value) : cachedRect;
|
||||
return cells;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
@@ -110,29 +111,38 @@ namespace OpenRA
|
||||
[FieldLoader.Ignore] public byte TileFormat = 1;
|
||||
public int2 MapSize;
|
||||
|
||||
[FieldLoader.Ignore] public Lazy<TileReference<ushort, byte>[,]> MapTiles;
|
||||
[FieldLoader.Ignore] public Lazy<TileReference<byte, byte>[,]> MapResources;
|
||||
[FieldLoader.Ignore] public int[,] CustomTerrain;
|
||||
[FieldLoader.Ignore] public Lazy<CellLayer<TerrainTile>> MapTiles;
|
||||
[FieldLoader.Ignore] public Lazy<CellLayer<ResourceTile>> MapResources;
|
||||
[FieldLoader.Ignore] public CellLayer<int> CustomTerrain;
|
||||
|
||||
[FieldLoader.Ignore] Lazy<Ruleset> rules;
|
||||
public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
|
||||
public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
|
||||
|
||||
[FieldLoader.Ignore] public CellRegion Cells;
|
||||
|
||||
public static Map FromTileset(TileSet tileset)
|
||||
{
|
||||
var tile = tileset.Templates.First();
|
||||
var tileRef = new TileReference<ushort, byte> { Type = tile.Key, Index = (byte)0 };
|
||||
var size = new Size(1, 1);
|
||||
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()
|
||||
{
|
||||
Title = "Name your map here",
|
||||
Description = "Describe your map here",
|
||||
Author = "Your name here",
|
||||
MapSize = new int2(1, 1),
|
||||
MapSize = new int2(size),
|
||||
Tileset = tileset.Id,
|
||||
Options = new MapOptions(),
|
||||
MapResources = Exts.Lazy(() => new TileReference<byte, byte>[1, 1]),
|
||||
MapTiles = Exts.Lazy(() => new TileReference<ushort, byte>[1, 1] { { tileRef } }),
|
||||
MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(size)),
|
||||
MapTiles = makeMapTiles,
|
||||
Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
|
||||
Smudges = Exts.Lazy(() => new List<SmudgeReference>())
|
||||
};
|
||||
@@ -229,11 +239,6 @@ namespace OpenRA
|
||||
NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications");
|
||||
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());
|
||||
MapResources = Exts.Lazy(() => LoadResourceTiles());
|
||||
|
||||
@@ -254,6 +259,14 @@ namespace OpenRA
|
||||
void PostInit()
|
||||
{
|
||||
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()
|
||||
@@ -346,9 +359,9 @@ namespace OpenRA
|
||||
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"))
|
||||
{
|
||||
if (dataStream.ReadUInt8() != 1)
|
||||
@@ -365,23 +378,27 @@ namespace OpenRA
|
||||
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3);
|
||||
var d = 0;
|
||||
for (var i = 0; i < MapSize.X; i++)
|
||||
{
|
||||
for (var j = 0; j < MapSize.Y; j++)
|
||||
{
|
||||
var tile = BitConverter.ToUInt16(data, d);
|
||||
d += 2;
|
||||
|
||||
var index = data[d++];
|
||||
if (index == byte.MaxValue)
|
||||
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;
|
||||
}
|
||||
|
||||
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"))
|
||||
{
|
||||
@@ -400,10 +417,11 @@ namespace OpenRA
|
||||
|
||||
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2);
|
||||
var d = 0;
|
||||
|
||||
// Load resource data
|
||||
for (var i = 0; i < MapSize.X; i++)
|
||||
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;
|
||||
@@ -423,38 +441,43 @@ namespace OpenRA
|
||||
for (var i = 0; i < MapSize.X; i++)
|
||||
for (var j = 0; j < MapSize.Y; j++)
|
||||
{
|
||||
writer.Write(MapTiles.Value[i, j].Type);
|
||||
writer.Write(MapTiles.Value[i, j].Index);
|
||||
var tile = MapTiles.Value[new CPos(i, j)];
|
||||
writer.Write(tile.Type);
|
||||
writer.Write(tile.Index);
|
||||
}
|
||||
|
||||
// Resource data
|
||||
for (var i = 0; i < MapSize.X; i++)
|
||||
{
|
||||
for (var j = 0; j < MapSize.Y; j++)
|
||||
{
|
||||
writer.Write(MapResources.Value[i, j].Type);
|
||||
writer.Write(MapResources.Value[i, j].Index);
|
||||
var tile = MapResources.Value[new CPos(i, j)];
|
||||
writer.Write(tile.Type);
|
||||
writer.Write(tile.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataStream.ToArray();
|
||||
}
|
||||
|
||||
public bool IsInMap(CPos xy) { return IsInMap(xy.X, xy.Y); }
|
||||
public bool IsInMap(int x, int y) { return Bounds.Contains(x, y); }
|
||||
public bool Contains(CPos xy) { return Bounds.Contains(xy.X, xy.Y); }
|
||||
|
||||
public void Resize(int width, int height) // editor magic.
|
||||
{
|
||||
var oldMapTiles = MapTiles.Value;
|
||||
var oldMapResources = MapResources.Value;
|
||||
var newSize = new Size(width, height);
|
||||
|
||||
MapTiles = Exts.Lazy(() => Exts.ResizeArray(oldMapTiles, oldMapTiles[0, 0], width, height));
|
||||
MapResources = Exts.Lazy(() => Exts.ResizeArray(oldMapResources, oldMapResources[0, 0], width, height));
|
||||
MapSize = new int2(width, height);
|
||||
MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0]));
|
||||
MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0]));
|
||||
MapSize = new int2(newSize);
|
||||
}
|
||||
|
||||
public void ResizeCordon(int left, int top, int right, int 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()
|
||||
@@ -524,26 +547,30 @@ namespace OpenRA
|
||||
{
|
||||
for (var i = Bounds.Left; i < Bounds.Right; i++)
|
||||
{
|
||||
var tr = MapTiles.Value[i, j];
|
||||
if (!tileset.Templates.ContainsKey(tr.Type))
|
||||
var cell = new CPos(i, j);
|
||||
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;
|
||||
}
|
||||
var template = tileset.Templates[tr.Type];
|
||||
|
||||
var template = tileset.Templates[type];
|
||||
if (!template.PickAny)
|
||||
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)
|
||||
{
|
||||
var custom = CustomTerrain[cell.X, cell.Y];
|
||||
var custom = CustomTerrain[cell];
|
||||
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)
|
||||
@@ -606,12 +633,12 @@ namespace OpenRA
|
||||
if (range >= TilesByDistance.Length)
|
||||
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;
|
||||
if (Bounds.Contains(t.X, t.Y))
|
||||
if (Contains(t))
|
||||
yield return t;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,29 @@
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public struct TileReference<T, U>
|
||||
public struct TerrainTile
|
||||
{
|
||||
public T Type;
|
||||
public U Index;
|
||||
public readonly ushort Type;
|
||||
public readonly byte Index;
|
||||
|
||||
public TileReference(T t, U i)
|
||||
public TerrainTile(ushort type, byte index)
|
||||
{
|
||||
Type = t;
|
||||
Index = i;
|
||||
Type = type;
|
||||
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(); }
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace OpenRA
|
||||
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];
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace OpenRA
|
||||
root.WriteToFile(filepath);
|
||||
}
|
||||
|
||||
public TerrainTypeInfo GetTerrainInfo(TileReference<ushort, byte> r)
|
||||
public TerrainTypeInfo GetTerrainInfo(TerrainTile r)
|
||||
{
|
||||
return terrainInfo[GetTerrainIndex(r)];
|
||||
}
|
||||
|
||||
@@ -235,6 +235,8 @@
|
||||
<Compile Include="Support\MersenneTwister.cs" />
|
||||
<Compile Include="GameInformation.cs" />
|
||||
<Compile Include="Widgets\RootWidget.cs" />
|
||||
<Compile Include="Map\CellLayer.cs" />
|
||||
<Compile Include="Map\CellRegion.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||
@@ -358,4 +360,4 @@
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace OpenRA.Orders
|
||||
|
||||
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();
|
||||
foreach (var subject in subjects)
|
||||
@@ -66,6 +66,6 @@ namespace OpenRA.Orders
|
||||
public virtual void Tick(World world) { }
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
||||
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 Map map;
|
||||
InfluenceNode[,] influence;
|
||||
readonly CellLayer<InfluenceNode> influence;
|
||||
|
||||
List<Actor>[] actors;
|
||||
int rows, cols;
|
||||
@@ -56,7 +56,7 @@ namespace OpenRA.Traits
|
||||
{
|
||||
this.info = info;
|
||||
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;
|
||||
rows = world.Map.MapSize.Y / info.BinSize + 1;
|
||||
@@ -71,20 +71,20 @@ namespace OpenRA.Traits
|
||||
|
||||
public IEnumerable<Actor> GetUnitsAt(CPos a)
|
||||
{
|
||||
if (!map.IsInMap(a))
|
||||
if (!map.Contains(a))
|
||||
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)
|
||||
yield return i.Actor;
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
|
||||
{
|
||||
if (!map.IsInMap(a))
|
||||
if (!map.Contains(a))
|
||||
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))
|
||||
yield return i.Actor;
|
||||
}
|
||||
@@ -107,12 +107,12 @@ namespace OpenRA.Traits
|
||||
|
||||
public bool AnyUnitsAt(CPos a)
|
||||
{
|
||||
return influence[a.X, a.Y] != null;
|
||||
return influence[a] != null;
|
||||
}
|
||||
|
||||
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)
|
||||
return true;
|
||||
|
||||
@@ -122,20 +122,25 @@ namespace OpenRA.Traits
|
||||
public void AddInfluence(Actor self, IOccupySpace ios)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (influenceNode == null)
|
||||
return;
|
||||
else if (influenceNode.Actor == toRemove)
|
||||
|
||||
if (influenceNode.Actor == toRemove)
|
||||
influenceNode = influenceNode.Next;
|
||||
|
||||
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();
|
||||
|
||||
World world;
|
||||
protected CellContents[,] content;
|
||||
protected CellContents[,] render;
|
||||
protected CellLayer<CellContents> content;
|
||||
protected CellLayer<CellContents> render;
|
||||
List<CPos> dirty;
|
||||
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var clip = wr.Viewport.CellBounds;
|
||||
for (var x = clip.Left; x < clip.Right; x++)
|
||||
foreach (var cell in wr.Viewport.VisibleCells)
|
||||
{
|
||||
for (var y = clip.Top; y < clip.Bottom; y++)
|
||||
{
|
||||
var pos = new CPos(x, y);
|
||||
if (world.ShroudObscures(pos))
|
||||
continue;
|
||||
if (world.ShroudObscures(cell))
|
||||
continue;
|
||||
|
||||
var c = render[x, y];
|
||||
if (c.Sprite != null)
|
||||
new SpriteRenderable(c.Sprite, pos.CenterPosition,
|
||||
WVec.Zero, -511, c.Type.Palette, 1f, true).Render(wr);
|
||||
}
|
||||
var c = render[cell];
|
||||
if (c.Sprite != null)
|
||||
new SpriteRenderable(c.Sprite, cell.CenterPosition,
|
||||
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;
|
||||
for (var u = -1; u < 2; u++)
|
||||
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;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
this.world = w;
|
||||
content = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];
|
||||
render = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];
|
||||
content = new CellLayer<CellContents>(w.Map);
|
||||
render = new CellLayer<CellContents>(w.Map);
|
||||
dirty = new List<CPos>();
|
||||
|
||||
var resources = w.WorldActor.TraitsImplementing<ResourceType>()
|
||||
.ToDictionary(r => r.Info.ResourceType, r => r);
|
||||
|
||||
var map = w.Map;
|
||||
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 cell = new CPos(x, y);
|
||||
ResourceType t;
|
||||
if (!resources.TryGetValue(w.Map.MapResources.Value[x, y].Type, out t))
|
||||
continue;
|
||||
ResourceType t;
|
||||
if (!resources.TryGetValue(w.Map.MapResources.Value[cell].Type, out t))
|
||||
continue;
|
||||
|
||||
if (!AllowResourceAt(t, cell))
|
||||
continue;
|
||||
if (!AllowResourceAt(t, cell))
|
||||
continue;
|
||||
|
||||
content[x, y] = CreateResourceCell(t, cell);
|
||||
}
|
||||
content[cell] = CreateResourceCell(t, cell);
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (type != null)
|
||||
{
|
||||
var type = content[x, y].Type;
|
||||
if (type != null)
|
||||
{
|
||||
// Adjacent includes the current cell, so is always >= 1
|
||||
var adjacent = GetAdjacentCellsWith(type, x, y);
|
||||
var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9);
|
||||
content[x, y].Density = Math.Max(density, 1);
|
||||
// Adjacent includes the current cell, so is always >= 1
|
||||
var adjacent = GetAdjacentCellsWith(type, cell);
|
||||
var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9);
|
||||
var temp = content[cell];
|
||||
temp.Density = Math.Max(density, 1);
|
||||
|
||||
render[x, y] = content[x, y];
|
||||
UpdateRenderedSprite(new CPos(x, y));
|
||||
}
|
||||
render[cell] = content[cell] = temp;
|
||||
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)
|
||||
{
|
||||
var sprites = t.Type.Variants[t.Variant];
|
||||
@@ -114,7 +103,7 @@ namespace OpenRA.Traits
|
||||
else
|
||||
t.Sprite = null;
|
||||
|
||||
render[p.X, p.Y] = t;
|
||||
render[cell] = t;
|
||||
}
|
||||
|
||||
protected virtual string ChooseRandomVariant(ResourceType t)
|
||||
@@ -129,7 +118,7 @@ namespace OpenRA.Traits
|
||||
{
|
||||
if (!self.World.FogObscures(c))
|
||||
{
|
||||
render[c.X, c.Y] = content[c.X, c.Y];
|
||||
render[c] = content[c];
|
||||
UpdateRenderedSprite(c);
|
||||
remove.Add(c);
|
||||
}
|
||||
@@ -139,15 +128,15 @@ namespace OpenRA.Traits
|
||||
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;
|
||||
|
||||
if (!rt.Info.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(a).Type))
|
||||
if (!rt.Info.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type))
|
||||
return false;
|
||||
|
||||
if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(a))
|
||||
if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(cell))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -160,9 +149,10 @@ namespace OpenRA.Traits
|
||||
|| (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
|
||||
{
|
||||
Type = t,
|
||||
@@ -172,7 +162,7 @@ namespace OpenRA.Traits
|
||||
|
||||
public void AddResource(ResourceType t, CPos p, int n)
|
||||
{
|
||||
var cell = content[p.X, p.Y];
|
||||
var cell = content[p];
|
||||
if (cell.Type == null)
|
||||
cell = CreateResourceCell(t, p);
|
||||
|
||||
@@ -180,58 +170,60 @@ namespace OpenRA.Traits
|
||||
return;
|
||||
|
||||
cell.Density = Math.Min(cell.Type.Info.MaxDensity, cell.Density + n);
|
||||
content[p.X, p.Y] = cell;
|
||||
content[p] = cell;
|
||||
|
||||
if (!dirty.Contains(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;
|
||||
if (type == null)
|
||||
var c = content[cell];
|
||||
if (c.Type == null)
|
||||
return null;
|
||||
|
||||
if (--content[p.X, p.Y].Density < 0)
|
||||
if (--c.Density < 0)
|
||||
{
|
||||
content[p.X, p.Y] = EmptyCell;
|
||||
world.Map.CustomTerrain[p.X, p.Y] = -1;
|
||||
content[cell] = EmptyCell;
|
||||
world.Map.CustomTerrain[cell] = -1;
|
||||
}
|
||||
else
|
||||
content[cell] = c;
|
||||
|
||||
if (!dirty.Contains(p))
|
||||
dirty.Add(p);
|
||||
if (!dirty.Contains(cell))
|
||||
dirty.Add(cell);
|
||||
|
||||
return type;
|
||||
return c.Type;
|
||||
}
|
||||
|
||||
public void Destroy(CPos p)
|
||||
public void Destroy(CPos cell)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Clear cell
|
||||
content[p.X, p.Y] = EmptyCell;
|
||||
world.Map.CustomTerrain[p.X, p.Y] = -1;
|
||||
content[cell] = EmptyCell;
|
||||
world.Map.CustomTerrain[cell] = -1;
|
||||
|
||||
if (!dirty.Contains(p))
|
||||
dirty.Add(p);
|
||||
if (!dirty.Contains(cell))
|
||||
dirty.Add(cell);
|
||||
}
|
||||
|
||||
public ResourceType GetResource(CPos p) { return content[p.X, p.Y].Type; }
|
||||
public ResourceType GetRenderedResource(CPos p) { return render[p.X, p.Y].Type; }
|
||||
public int GetResourceDensity(CPos p) { return content[p.X, p.Y].Density; }
|
||||
public int GetMaxResourceDensity(CPos p)
|
||||
public ResourceType GetResource(CPos cell) { return content[cell].Type; }
|
||||
public ResourceType GetRenderedResource(CPos cell) { return render[cell].Type; }
|
||||
public int GetResourceDensity(CPos cell) { return content[cell].Density; }
|
||||
public int GetMaxResourceDensity(CPos cell)
|
||||
{
|
||||
if (content[p.X, p.Y].Type == null)
|
||||
if (content[cell].Type == null)
|
||||
return 0;
|
||||
|
||||
return content[p.X, p.Y].Type.Info.MaxDensity;
|
||||
return content[cell].Type.Info.MaxDensity;
|
||||
}
|
||||
|
||||
public struct CellContents
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace OpenRA.Traits
|
||||
Actor self;
|
||||
Map map;
|
||||
|
||||
int[,] visibleCount;
|
||||
int[,] generatedShroudCount;
|
||||
bool[,] explored;
|
||||
CellLayer<int> visibleCount;
|
||||
CellLayer<int> generatedShroudCount;
|
||||
CellLayer<bool> explored;
|
||||
|
||||
readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;
|
||||
|
||||
@@ -38,8 +38,6 @@ namespace OpenRA.Traits
|
||||
Dictionary<Actor, CPos[]> visibility = 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 Shroud(Actor self)
|
||||
@@ -47,9 +45,9 @@ namespace OpenRA.Traits
|
||||
this.self = self;
|
||||
map = self.World.Map;
|
||||
|
||||
visibleCount = new int[map.MapSize.X, map.MapSize.Y];
|
||||
generatedShroudCount = new int[map.MapSize.X, map.MapSize.Y];
|
||||
explored = new bool[map.MapSize.X, map.MapSize.Y];
|
||||
visibleCount = new CellLayer<int>(map);
|
||||
generatedShroudCount = new CellLayer<int>(map);
|
||||
explored = new CellLayer<bool>(map);
|
||||
|
||||
self.World.ActorAdded += AddVisibility;
|
||||
self.World.ActorRemoved += RemoveVisibility;
|
||||
@@ -57,9 +55,6 @@ namespace OpenRA.Traits
|
||||
self.World.ActorAdded += AddShroudGeneration;
|
||||
self.World.ActorRemoved += RemoveShroudGeneration;
|
||||
|
||||
if (!self.World.LobbyInfo.GlobalSettings.Shroud)
|
||||
ExploredBounds = map.Bounds;
|
||||
|
||||
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
|
||||
}
|
||||
|
||||
@@ -71,15 +66,12 @@ namespace OpenRA.Traits
|
||||
static IEnumerable<CPos> FindVisibleTiles(World world, CPos position, WRange radius)
|
||||
{
|
||||
var r = (radius.Range + 1023) / 1024;
|
||||
var min = (position - new CVec(r, r)).Clamp(world.Map.Bounds);
|
||||
var max = (position + new CVec(r, r)).Clamp(world.Map.Bounds);
|
||||
|
||||
var circleArea = radius.Range * radius.Range;
|
||||
var limit = radius.Range * radius.Range;
|
||||
var pos = position.CenterPosition;
|
||||
for (var j = min.Y; j <= max.Y; j++)
|
||||
for (var i = min.X; i <= max.X; i++)
|
||||
if (circleArea >= (new CPos(i, j).CenterPosition - pos).LengthSquared)
|
||||
yield return new CPos(i, j);
|
||||
|
||||
foreach (var cell in world.Map.FindTilesInCircle(position, r))
|
||||
if ((cell.CenterPosition - pos).HorizontalLengthSquared <= limit)
|
||||
yield return cell;
|
||||
}
|
||||
|
||||
void AddVisibility(Actor a)
|
||||
@@ -92,20 +84,11 @@ namespace OpenRA.Traits
|
||||
var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range))
|
||||
.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
|
||||
foreach (var c in visible)
|
||||
{
|
||||
visibleCount[c.X, c.Y]++;
|
||||
explored[c.X, c.Y] = true;
|
||||
visibleCount[c]++;
|
||||
explored[c] = true;
|
||||
}
|
||||
|
||||
if (visibility.ContainsKey(a))
|
||||
@@ -122,7 +105,7 @@ namespace OpenRA.Traits
|
||||
return;
|
||||
|
||||
foreach (var c in visible)
|
||||
visibleCount[c.X, c.Y]--;
|
||||
visibleCount[c]--;
|
||||
|
||||
visibility.Remove(a);
|
||||
Invalidate();
|
||||
@@ -147,7 +130,7 @@ namespace OpenRA.Traits
|
||||
var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range))
|
||||
.Distinct().ToArray();
|
||||
foreach (var c in shrouded)
|
||||
generatedShroudCount[c.X, c.Y]++;
|
||||
generatedShroudCount[c]++;
|
||||
|
||||
if (generation.ContainsKey(a))
|
||||
throw new InvalidOperationException("Attempting to add duplicate shroud generation");
|
||||
@@ -163,7 +146,7 @@ namespace OpenRA.Traits
|
||||
return;
|
||||
|
||||
foreach (var c in shrouded)
|
||||
generatedShroudCount[c.X, c.Y]--;
|
||||
generatedShroudCount[c]--;
|
||||
|
||||
generation.Remove(a);
|
||||
Invalidate();
|
||||
@@ -203,55 +186,44 @@ namespace OpenRA.Traits
|
||||
public void Explore(World world, CPos center, WRange range)
|
||||
{
|
||||
foreach (var q in FindVisibleTiles(world, center, range))
|
||||
explored[q.X, q.Y] = 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);
|
||||
explored[q] = true;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void Explore(Shroud s)
|
||||
{
|
||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||
if (s.explored[i,j] == true)
|
||||
explored[i, j] = true;
|
||||
foreach (var cell in map.Cells)
|
||||
if (s.explored[cell])
|
||||
explored[cell] = true;
|
||||
|
||||
ExploredBounds = Rectangle.Union(ExploredBounds, s.ExploredBounds);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void ExploreAll(World world)
|
||||
{
|
||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||
explored[i, j] = true;
|
||||
|
||||
ExploredBounds = world.Map.Bounds;
|
||||
explored.Clear(true);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void ResetExploration()
|
||||
{
|
||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||
explored[i, j] = visibleCount[i, j] > 0;
|
||||
foreach (var cell in map.Cells)
|
||||
explored[cell] = visibleCount[cell] > 0;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public bool IsExplored(CPos xy) { return IsExplored(xy.X, xy.Y); }
|
||||
public bool IsExplored(int x, int y)
|
||||
public bool IsExplored(CPos cell)
|
||||
{
|
||||
if (!map.IsInMap(x, y))
|
||||
if (!map.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Shroud)
|
||||
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)
|
||||
@@ -259,16 +231,15 @@ namespace OpenRA.Traits
|
||||
return GetVisOrigins(a).Any(o => IsExplored(o));
|
||||
}
|
||||
|
||||
public bool IsVisible(CPos xy) { return IsVisible(xy.X, xy.Y); }
|
||||
public bool IsVisible(int x, int y)
|
||||
public bool IsVisible(CPos cell)
|
||||
{
|
||||
if (!map.IsInMap(x, y))
|
||||
if (!map.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog)
|
||||
return true;
|
||||
|
||||
return visibleCount[x, y] > 0;
|
||||
return visibleCount[cell] > 0;
|
||||
}
|
||||
|
||||
// Actors are hidden under shroud, but not under fog by default
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace OpenRA.Widgets
|
||||
{
|
||||
TooltipType = WorldTooltipType.None;
|
||||
var cell = worldRenderer.Position(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos)).ToCPos();
|
||||
if (!world.Map.IsInMap(cell))
|
||||
if (!world.Map.Contains(cell))
|
||||
return;
|
||||
|
||||
if (world.ShroudObscures(cell))
|
||||
|
||||
@@ -209,18 +209,20 @@ namespace OpenRA.Widgets
|
||||
}
|
||||
else if (key == Game.Settings.Keys.SelectUnitsByTypeKey)
|
||||
{
|
||||
var selectedTypes = World.Selection.Actors.Where(
|
||||
x => x.Owner == World.RenderPlayer).Select(a => a.Info);
|
||||
var selectedTypes = World.Selection.Actors
|
||||
.Where(x => x.Owner == World.RenderPlayer)
|
||||
.Select(a => a.Info);
|
||||
|
||||
Func<Actor, bool> cond = a => a.Owner == World.RenderPlayer && selectedTypes.Contains(a.Info);
|
||||
var newSelection = SelectActorsInBox(
|
||||
World, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight, cond);
|
||||
var tl = worldRenderer.Viewport.TopLeft;
|
||||
var br = worldRenderer.Viewport.BottomRight;
|
||||
var newSelection = SelectActorsInBox(World, tl, br, cond);
|
||||
|
||||
if (newSelection.Count() > selectedTypes.Count())
|
||||
Game.Debug("Selected across screen");
|
||||
else
|
||||
{
|
||||
newSelection = World.ActorMap.ActorsInBox(
|
||||
World.Map.Bounds.TopLeftAsCPos().TopLeft,
|
||||
World.Map.Bounds.BottomRightAsCPos().BottomRight).Where(cond);
|
||||
newSelection = World.ActorMap.ActorsInWorld().Where(cond);
|
||||
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(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
|
||||
{
|
||||
get { return orderManager.Connection is ReplayConnection; }
|
||||
|
||||
@@ -97,28 +97,28 @@ namespace OpenRA.Traits
|
||||
ClearSides FindClearSides(ResourceType t, CPos p)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (render[p.X - 1, p.Y - 1].Type != t)
|
||||
if (render[p + new CVec(-1, -1)].Type != t)
|
||||
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;
|
||||
|
||||
if (render[p.X - 1, p.Y + 1].Type != t)
|
||||
if (render[p + new CVec(-1, 1)].Type != t)
|
||||
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;
|
||||
|
||||
return ret;
|
||||
@@ -126,7 +126,7 @@ namespace OpenRA.Traits
|
||||
|
||||
void UpdateRenderedTileInner(CPos p)
|
||||
{
|
||||
var t = render[p.X, p.Y];
|
||||
var t = render[p];
|
||||
if (t.Density > 0)
|
||||
{
|
||||
var clear = FindClearSides(t.Type, p);
|
||||
@@ -146,7 +146,7 @@ namespace OpenRA.Traits
|
||||
else
|
||||
t.Sprite = null;
|
||||
|
||||
render[p.X, p.Y] = t;
|
||||
render[p] = t;
|
||||
}
|
||||
|
||||
protected override void UpdateRenderedSprite(CPos p)
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
var path = pathFinder.FindBidiPath(
|
||||
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);
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
|
||||
public bool CanLand(CPos cell)
|
||||
{
|
||||
if (!self.World.Map.IsInMap(cell))
|
||||
if (!self.World.Map.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (self.World.ActorMap.AnyUnitsAt(cell))
|
||||
@@ -246,7 +246,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
{
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled || !self.World.Map.IsInMap(self.Location))
|
||||
if (IsCanceled || !self.World.Map.Contains(self.Location))
|
||||
return NextActivity;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!self.World.Map.IsInMap(location))
|
||||
if (!self.World.Map.Contains(location))
|
||||
return false;
|
||||
|
||||
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
|
||||
|
||||
@@ -91,14 +91,14 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
// Set the initial custom terrain types
|
||||
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)
|
||||
{
|
||||
var dx = cell - self.Location;
|
||||
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)
|
||||
@@ -121,7 +121,7 @@ namespace OpenRA.Mods.RA
|
||||
IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
// Update map
|
||||
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,
|
||||
// update the domain index.
|
||||
|
||||
@@ -19,95 +19,100 @@ namespace OpenRA.Mods.RA
|
||||
class BridgeLayerInfo : ITraitInfo
|
||||
{
|
||||
[ActorReference]
|
||||
public readonly string[] Bridges = {"bridge1", "bridge2"};
|
||||
public readonly string[] Bridges = { "bridge1", "bridge2" };
|
||||
|
||||
public object Create(ActorInitializer init) { return new BridgeLayer(init.self, this); }
|
||||
}
|
||||
|
||||
class BridgeLayer : IWorldLoaded
|
||||
{
|
||||
readonly BridgeLayerInfo Info;
|
||||
readonly BridgeLayerInfo info;
|
||||
readonly World world;
|
||||
Dictionary<ushort, Pair<string, float>> BridgeTypes = new Dictionary<ushort, Pair<string,float>>();
|
||||
Bridge[,] Bridges;
|
||||
Dictionary<ushort, Pair<string, float>> bridgeTypes = new Dictionary<ushort, Pair<string, float>>();
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
foreach(var bridge in Info.Bridges)
|
||||
foreach (var bridge in info.Bridges)
|
||||
{
|
||||
var bi = w.Map.Rules.Actors[bridge].Traits.Get<BridgeInfo>();
|
||||
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
|
||||
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++)
|
||||
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
|
||||
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
|
||||
if (Bridges[i,j] != null)
|
||||
if (bridges[cell] != null)
|
||||
return;
|
||||
|
||||
// Correlate the tile "image" aka subtile with its position to find the template origin
|
||||
var tile = w.Map.MapTiles.Value[i, j].Type;
|
||||
var index = w.Map.MapTiles.Value[i, j].Index;
|
||||
var tile = w.Map.MapTiles.Value[cell].Type;
|
||||
var index = w.Map.MapTiles.Value[cell].Index;
|
||||
var template = w.TileSet.Templates[tile];
|
||||
var ni = i - index % template.Size.X;
|
||||
var nj = j - index / template.Size.X;
|
||||
var ni = cell.X - 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
|
||||
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 OwnerInit(w.WorldActor.Owner),
|
||||
new HealthInit(BridgeTypes[tile].Second),
|
||||
new HealthInit(bridgeTypes[tile].Second),
|
||||
}).Trait<Bridge>();
|
||||
|
||||
var subTiles = new Dictionary<CPos, byte>();
|
||||
|
||||
// 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
|
||||
var x = ni + ind % template.Size.X;
|
||||
var y = nj + ind / template.Size.X;
|
||||
var subtile = new CPos(ni + ind % template.Size.X, nj + ind / template.Size.X);
|
||||
|
||||
// This isn't the bridge you're looking for
|
||||
if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].Type != tile ||
|
||||
w.Map.MapTiles.Value[x, y].Index != ind)
|
||||
if (!w.Map.Contains(subtile) || w.Map.MapTiles.Value[subtile].Type != tile ||
|
||||
w.Map.MapTiles.Value[subtile].Index != ind)
|
||||
continue;
|
||||
|
||||
subTiles.Add(new CPos(x, y), ind);
|
||||
Bridges[x,y] = bridge;
|
||||
subTiles.Add(subtile, ind);
|
||||
bridges[subtile] = bridge;
|
||||
}
|
||||
|
||||
bridge.Create(tile, subTiles);
|
||||
}
|
||||
|
||||
// Used to check for neighbouring bridges
|
||||
public Bridge GetBridge(CPos cell)
|
||||
{
|
||||
if (!world.Map.IsInMap(cell))
|
||||
if (!world.Map.Contains(cell))
|
||||
return null;
|
||||
|
||||
return Bridges[ cell.X, cell.Y ];
|
||||
return bridges[cell];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
|
||||
public class BuildingInfluence
|
||||
{
|
||||
Actor[,] influence;
|
||||
CellLayer<Actor> influence;
|
||||
Map map;
|
||||
|
||||
public BuildingInfluence(World world)
|
||||
{
|
||||
map = world.Map;
|
||||
|
||||
influence = new Actor[map.MapSize.X, map.MapSize.Y];
|
||||
influence = new CellLayer<Actor>(map);
|
||||
|
||||
world.ActorAdded += a =>
|
||||
{
|
||||
@@ -35,8 +35,8 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
return;
|
||||
|
||||
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)
|
||||
influence[u.X, u.Y] = a;
|
||||
if (map.Contains(u) && influence[u] == null)
|
||||
influence[u] = a;
|
||||
};
|
||||
|
||||
world.ActorRemoved += a =>
|
||||
@@ -46,17 +46,17 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
return;
|
||||
|
||||
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)
|
||||
influence[u.X, u.Y] = null;
|
||||
if (map.Contains(u) && influence[u] == a)
|
||||
influence[u] = null;
|
||||
};
|
||||
}
|
||||
|
||||
public Actor GetBuildingAt(CPos cell)
|
||||
{
|
||||
if (!map.IsInMap(cell))
|
||||
if (!map.Contains(cell))
|
||||
return null;
|
||||
|
||||
return influence[cell.X, cell.Y];
|
||||
return influence[cell];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +57,11 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
continue;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
@@ -77,10 +77,10 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
continue;
|
||||
|
||||
// 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;
|
||||
|
||||
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.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)
|
||||
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
|
||||
var res = world.WorldActor.Trait<ResourceLayer>();
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA
|
||||
var world = firedBy.World;
|
||||
var targetTile = pos.ToCPos();
|
||||
|
||||
if (!world.Map.IsInMap(targetTile))
|
||||
if (!world.Map.Contains(targetTile))
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
if (!info.TerrainTypes.Contains(type))
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace OpenRA.Mods.RA.Effects
|
||||
|| (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) // Within range
|
||||
|| (info.RangeLimit != 0 && ticks > info.RangeLimit) // Ran out of fuel
|
||||
|| (!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
|
||||
|
||||
if (shouldExplode)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.RA
|
||||
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); }
|
||||
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;
|
||||
|
||||
if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type))
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace OpenRA.Mods.RA
|
||||
return false;
|
||||
|
||||
var location = target.CenterPosition.ToCPos();
|
||||
if (!self.World.Map.IsInMap(location))
|
||||
if (!self.World.Map.Contains(location))
|
||||
return false;
|
||||
|
||||
cursor = "ability";
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public int MovementCostForCell(World world, CPos cell)
|
||||
{
|
||||
if (!world.Map.IsInMap(cell.X, cell.Y))
|
||||
if (!world.Map.Contains(cell))
|
||||
return int.MaxValue;
|
||||
|
||||
var index = world.Map.GetTerrainIndex(cell);
|
||||
@@ -580,7 +580,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
if (self.Owner.Shroud.IsExplored(location))
|
||||
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))
|
||||
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.Diagnostics;
|
||||
using System.Linq;
|
||||
using OpenRA;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
@@ -68,7 +69,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
var pb = FindBidiPath(
|
||||
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);
|
||||
@@ -109,7 +110,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
var path = FindBidiPath(
|
||||
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;
|
||||
@@ -124,21 +125,19 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
List<CPos> path = null;
|
||||
|
||||
while (!search.queue.Empty)
|
||||
while (!search.Queue.Empty)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
|
||||
if (dbg != null)
|
||||
{
|
||||
dbg.AddLayer(search.considered.Select(p => new Pair<CPos, int>(p, search.cellInfo[p.X, p.Y].MinCost)), search.maxCost, search.owner);
|
||||
}
|
||||
dbg.AddLayer(search.Considered.Select(p => new Pair<CPos, int>(p, search.CellInfo[p].MinCost)), search.MaxCost, search.Owner);
|
||||
|
||||
if (path != null)
|
||||
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 pathNode = destination;
|
||||
|
||||
while (cellInfo[pathNode.X, pathNode.Y].Path != pathNode)
|
||||
while (cellInfo[pathNode].Path != pathNode)
|
||||
{
|
||||
ret.Add(pathNode);
|
||||
pathNode = cellInfo[pathNode.X, pathNode.Y].Path;
|
||||
pathNode = cellInfo[pathNode].Path;
|
||||
}
|
||||
|
||||
ret.Add(pathNode);
|
||||
@@ -165,9 +164,8 @@ namespace OpenRA.Mods.RA.Move
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<CPos> FindBidiPath( /* searches from both ends toward each other */
|
||||
PathSearch fromSrc,
|
||||
PathSearch fromDest)
|
||||
// Searches from both ends toward each other
|
||||
public List<CPos> FindBidiPath(PathSearch fromSrc, PathSearch fromDest)
|
||||
{
|
||||
using (new PerfSample("Pathfinder"))
|
||||
{
|
||||
@@ -176,13 +174,13 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
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 */
|
||||
var p = fromSrc.Expand(world);
|
||||
|
||||
if (fromDest.cellInfo[p.X, p.Y].Seen &&
|
||||
fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity)
|
||||
if (fromDest.CellInfo[p].Seen &&
|
||||
fromDest.CellInfo[p].MinCost < float.PositiveInfinity)
|
||||
{
|
||||
path = MakeBidiPath(fromSrc, fromDest, p);
|
||||
break;
|
||||
@@ -191,8 +189,8 @@ namespace OpenRA.Mods.RA.Move
|
||||
/* make some progress on the second search */
|
||||
var q = fromDest.Expand(world);
|
||||
|
||||
if (fromSrc.cellInfo[q.X, q.Y].Seen &&
|
||||
fromSrc.cellInfo[q.X, q.Y].MinCost < float.PositiveInfinity)
|
||||
if (fromSrc.CellInfo[q].Seen &&
|
||||
fromSrc.CellInfo[q].MinCost < float.PositiveInfinity)
|
||||
{
|
||||
path = MakeBidiPath(fromSrc, fromDest, q);
|
||||
break;
|
||||
@@ -202,8 +200,8 @@ namespace OpenRA.Mods.RA.Move
|
||||
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
|
||||
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(fromDest.considered.Select(p => new Pair<CPos, int>(p, fromDest.cellInfo[p.X, p.Y].MinCost)), fromDest.maxCost, fromDest.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].MinCost)), fromDest.MaxCost, fromDest.Owner);
|
||||
}
|
||||
|
||||
if (path != null)
|
||||
@@ -216,25 +214,25 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
static List<CPos> MakeBidiPath(PathSearch a, PathSearch b, CPos p)
|
||||
{
|
||||
var ca = a.cellInfo;
|
||||
var cb = b.cellInfo;
|
||||
var ca = a.CellInfo;
|
||||
var cb = b.CellInfo;
|
||||
|
||||
var ret = new List<CPos>();
|
||||
|
||||
var q = p;
|
||||
while (ca[q.X, q.Y].Path != q)
|
||||
while (ca[q].Path != q)
|
||||
{
|
||||
ret.Add(q);
|
||||
q = ca[q.X, q.Y].Path;
|
||||
q = ca[q].Path;
|
||||
}
|
||||
ret.Add(q);
|
||||
|
||||
ret.Reverse();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -286,8 +284,8 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public struct PathDistance : IComparable<PathDistance>
|
||||
{
|
||||
public int EstTotal;
|
||||
public CPos Location;
|
||||
public readonly int EstTotal;
|
||||
public readonly CPos Location;
|
||||
|
||||
public PathDistance(int estTotal, CPos location)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#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
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
@@ -10,45 +10,97 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
public sealed class PathSearch : IDisposable
|
||||
{
|
||||
World world;
|
||||
public CellInfo[,] cellInfo;
|
||||
public PriorityQueue<PathDistance> queue;
|
||||
public Func<CPos, int> heuristic;
|
||||
public CellLayer<CellInfo> CellInfo;
|
||||
public PriorityQueue<PathDistance> Queue;
|
||||
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, bool> customBlock;
|
||||
public bool checkForBlocked;
|
||||
public Actor ignoreBuilding;
|
||||
public bool inReverse;
|
||||
public HashSet<CPos> considered;
|
||||
public int maxCost;
|
||||
Pair<CVec, int>[] nextDirections;
|
||||
MobileInfo mobileInfo;
|
||||
Actor self;
|
||||
public Player owner { get { return self.Owner; } }
|
||||
int laneBias = 1;
|
||||
|
||||
public PathSearch(World world, MobileInfo mobileInfo, Actor self)
|
||||
{
|
||||
this.world = world;
|
||||
cellInfo = InitCellInfo();
|
||||
this.self = self;
|
||||
CellInfo = InitCellInfo();
|
||||
this.mobileInfo = mobileInfo;
|
||||
this.self = self;
|
||||
customCost = null;
|
||||
queue = new PriorityQueue<PathDistance>();
|
||||
considered = new HashSet<CPos>();
|
||||
maxCost = 0;
|
||||
Queue = new PriorityQueue<PathDistance>();
|
||||
Considered = new HashSet<CPos>();
|
||||
MaxCost = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -60,13 +112,13 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public PathSearch WithIgnoredBuilding(Actor b)
|
||||
{
|
||||
ignoreBuilding = b;
|
||||
IgnoreBuilding = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PathSearch WithHeuristic(Func<CPos, int> h)
|
||||
{
|
||||
heuristic = h;
|
||||
Heuristic = h;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -78,7 +130,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public PathSearch WithoutLaneBias()
|
||||
{
|
||||
LaneBias = 0;
|
||||
laneBias = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -88,18 +140,20 @@ namespace OpenRA.Mods.RA.Move
|
||||
return this;
|
||||
}
|
||||
|
||||
int LaneBias = 1;
|
||||
|
||||
public CPos Expand(World world)
|
||||
{
|
||||
var p = queue.Pop();
|
||||
while (cellInfo[p.Location.X, p.Location.Y].Seen)
|
||||
if (queue.Empty)
|
||||
var p = Queue.Pop();
|
||||
while (CellInfo[p.Location].Seen)
|
||||
{
|
||||
if (Queue.Empty)
|
||||
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);
|
||||
|
||||
@@ -114,7 +168,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -124,9 +178,10 @@ namespace OpenRA.Mods.RA.Move
|
||||
var newHere = p.Location + d;
|
||||
|
||||
// Is this direction flat-out unusable or already seen?
|
||||
if (!world.Map.IsInMap(newHere.X, newHere.Y))
|
||||
if (!world.Map.Contains(newHere))
|
||||
continue;
|
||||
if (cellInfo[newHere.X, newHere.Y].Seen)
|
||||
|
||||
if (CellInfo[newHere].Seen)
|
||||
continue;
|
||||
|
||||
// Now we may seriously consider this direction using heuristics:
|
||||
@@ -135,18 +190,19 @@ namespace OpenRA.Mods.RA.Move
|
||||
if (costHere == int.MaxValue)
|
||||
continue;
|
||||
|
||||
if (!mobileInfo.CanEnterCell(world, self, newHere, ignoreBuilding, checkForBlocked, false))
|
||||
if (!mobileInfo.CanEnterCell(world, self, newHere, IgnoreBuilding, CheckForBlocked, false))
|
||||
continue;
|
||||
|
||||
if (customBlock != null && customBlock(newHere))
|
||||
continue;
|
||||
|
||||
var est = heuristic(newHere);
|
||||
var est = Heuristic(newHere);
|
||||
if (est == int.MaxValue)
|
||||
continue;
|
||||
|
||||
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;
|
||||
if (customCost != null)
|
||||
@@ -156,105 +212,82 @@ namespace OpenRA.Mods.RA.Move
|
||||
}
|
||||
|
||||
// directional bonuses for smoother flow!
|
||||
if (LaneBias != 0)
|
||||
if (laneBias != 0)
|
||||
{
|
||||
var ux = (newHere.X + (inReverse ? 1 : 0) & 1);
|
||||
var uy = (newHere.Y + (inReverse ? 1 : 0) & 1);
|
||||
var ux = newHere.X + (InReverse ? 1 : 0) & 1;
|
||||
var uy = newHere.Y + (InReverse ? 1 : 0) & 1;
|
||||
|
||||
if (ux == 0 && d.Y < 0) cellCost += LaneBias;
|
||||
else if (ux == 1 && d.Y > 0) cellCost += LaneBias;
|
||||
if (uy == 0 && d.X < 0) cellCost += LaneBias;
|
||||
else if (uy == 1 && d.X > 0) cellCost += LaneBias;
|
||||
if (ux == 0 && d.Y < 0)
|
||||
cellCost += laneBias;
|
||||
else if (ux == 1 && d.Y > 0)
|
||||
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:
|
||||
if (newCost > cellInfo[newHere.X, newHere.Y].MinCost)
|
||||
if (newCost > CellInfo[newHere].MinCost)
|
||||
continue;
|
||||
|
||||
cellInfo[newHere.X, newHere.Y].Path = p.Location;
|
||||
cellInfo[newHere.X, newHere.Y].MinCost = newCost;
|
||||
var hereCell = CellInfo[newHere];
|
||||
hereCell.Path = p.Location;
|
||||
hereCell.MinCost = newCost;
|
||||
CellInfo[newHere] = hereCell;
|
||||
|
||||
nextDirections[i].Second = newCost + est;
|
||||
queue.Add(new PathDistance(newCost + est, newHere));
|
||||
Queue.Add(new PathDistance(newCost + est, newHere));
|
||||
|
||||
if (newCost > maxCost) maxCost = newCost;
|
||||
considered.Add(newHere);
|
||||
if (newCost > MaxCost)
|
||||
MaxCost = newCost;
|
||||
|
||||
Considered.Add(newHere);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
public void AddInitialCell(CPos location)
|
||||
{
|
||||
if (!world.Map.IsInMap(location.X, location.Y))
|
||||
if (!self.World.Map.Contains(location))
|
||||
return;
|
||||
|
||||
cellInfo[location.X, location.Y] = new CellInfo(0, location, false);
|
||||
queue.Add(new PathDistance(heuristic(location), location));
|
||||
CellInfo[location] = new CellInfo(0, location, false);
|
||||
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)
|
||||
{
|
||||
checkForBlocked = checkForBlocked
|
||||
};
|
||||
return search;
|
||||
lock (CellInfoPool)
|
||||
return CellInfoPool.Dequeue();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
heuristic = DefaultEstimator(target),
|
||||
checkForBlocked = checkForBlocked
|
||||
};
|
||||
|
||||
search.AddInitialCell(from);
|
||||
return search;
|
||||
lock (CellInfoPool)
|
||||
CellInfoPool.Enqueue(ci);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
heuristic = DefaultEstimator(target),
|
||||
checkForBlocked = checkForBlocked
|
||||
};
|
||||
CellLayer<CellInfo> result = null;
|
||||
var mapSize = new Size(self.World.Map.MapSize.X, self.World.Map.MapSize.Y);
|
||||
|
||||
foreach (var sl in froms)
|
||||
search.AddInitialCell(sl);
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
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)
|
||||
// HACK: Uses a static cache so that double-ended searches (which have two PathSearch instances)
|
||||
// can implicitly share data. The PathFinder should allocate the CellInfo array and pass it
|
||||
// explicitly to the things that need to share it.
|
||||
while (CellInfoPool.Count > 0)
|
||||
{
|
||||
var cellInfo = GetFromPool();
|
||||
if (cellInfo.GetUpperBound(0) != world.Map.MapSize.X - 1 ||
|
||||
cellInfo.GetUpperBound(1) != world.Map.MapSize.Y - 1)
|
||||
if (cellInfo.Size != mapSize)
|
||||
{
|
||||
Log.Write("debug", "Discarding old pooled CellInfo of wrong size.");
|
||||
continue;
|
||||
@@ -265,36 +298,24 @@ namespace OpenRA.Mods.RA.Move
|
||||
}
|
||||
|
||||
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++)
|
||||
for (var y = 0; y < world.Map.MapSize.Y; y++)
|
||||
result[ x, y ] = new CellInfo( int.MaxValue, new CPos( x, y ), false );
|
||||
foreach (var cell in self.World.Map.Cells)
|
||||
result[cell] = new CellInfo(int.MaxValue, cell, false);
|
||||
|
||||
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;
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
|
||||
PutBackIntoPool(cellInfo);
|
||||
cellInfo = null;
|
||||
PutBackIntoPool(CellInfo);
|
||||
CellInfo = null;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA
|
||||
return false;
|
||||
|
||||
var location = target.CenterPosition.ToCPos();
|
||||
if (self.World.Map.IsInMap(location))
|
||||
if (self.World.Map.Contains(location))
|
||||
{
|
||||
cursor = "ability";
|
||||
return true;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Render
|
||||
if (self.CenterPosition.Z > 0 || move.IsMoving)
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -35,36 +36,41 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public class ShroudRenderer : IRenderShroud, IWorldLoaded
|
||||
{
|
||||
struct ShroudTile
|
||||
class ShroudTile
|
||||
{
|
||||
public CPos Position;
|
||||
public float2 ScreenPosition;
|
||||
public int Variant;
|
||||
public readonly CPos Position;
|
||||
public readonly float2 ScreenPosition;
|
||||
public readonly int Variant;
|
||||
|
||||
public Sprite Fog;
|
||||
public Sprite Shroud;
|
||||
|
||||
public ShroudTile(CPos position, float2 screenPosition, int variant)
|
||||
{
|
||||
Position = position;
|
||||
ScreenPosition = screenPosition;
|
||||
Variant = variant;
|
||||
}
|
||||
}
|
||||
|
||||
ShroudRendererInfo info;
|
||||
Sprite[] sprites;
|
||||
Sprite unexploredTile;
|
||||
int[] spriteMap;
|
||||
|
||||
ShroudTile[] tiles;
|
||||
int tileStride, variantStride;
|
||||
CellLayer<ShroudTile> tiles;
|
||||
int variantStride;
|
||||
|
||||
int shroudHash;
|
||||
PaletteReference fogPalette, shroudPalette;
|
||||
Rectangle bounds;
|
||||
bool useExtendedIndex;
|
||||
Map map;
|
||||
|
||||
public ShroudRenderer(World world, ShroudRendererInfo info)
|
||||
{
|
||||
var map = world.Map;
|
||||
bounds = map.Bounds;
|
||||
useExtendedIndex = info.UseExtendedIndex;
|
||||
this.info = info;
|
||||
map = world.Map;
|
||||
|
||||
tiles = new ShroudTile[map.MapSize.X * map.MapSize.Y];
|
||||
tileStride = map.MapSize.X;
|
||||
tiles = new CellLayer<ShroudTile>(map);
|
||||
|
||||
// Force update on first render
|
||||
shroudHash = -1;
|
||||
@@ -80,14 +86,10 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
|
||||
// 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++)
|
||||
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
|
||||
if (!info.Index.Contains(0))
|
||||
{
|
||||
@@ -102,21 +104,21 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
static int FoggedEdges(Shroud s, CPos p, bool useExtendedIndex)
|
||||
{
|
||||
if (!s.IsVisible(p.X, p.Y))
|
||||
if (!s.IsVisible(p))
|
||||
return 15;
|
||||
|
||||
// If a side is shrouded then we also count the corners
|
||||
var u = 0;
|
||||
if (!s.IsVisible(p.X, p.Y - 1)) u |= 0x13;
|
||||
if (!s.IsVisible(p.X + 1, p.Y)) u |= 0x26;
|
||||
if (!s.IsVisible(p.X, p.Y + 1)) u |= 0x4C;
|
||||
if (!s.IsVisible(p.X - 1, p.Y)) u |= 0x89;
|
||||
if (!s.IsVisible(p + new CVec(0, -1))) u |= 0x13;
|
||||
if (!s.IsVisible(p + new CVec(1, 0))) u |= 0x26;
|
||||
if (!s.IsVisible(p + new CVec(0, 1))) u |= 0x4C;
|
||||
if (!s.IsVisible(p + new CVec(-1, 0))) u |= 0x89;
|
||||
|
||||
var uside = u & 0x0F;
|
||||
if (!s.IsVisible(p.X - 1, p.Y - 1)) u |= 0x01;
|
||||
if (!s.IsVisible(p.X + 1, p.Y - 1)) u |= 0x02;
|
||||
if (!s.IsVisible(p.X + 1, p.Y + 1)) u |= 0x04;
|
||||
if (!s.IsVisible(p.X - 1, p.Y + 1)) u |= 0x08;
|
||||
if (!s.IsVisible(p + new CVec(-1, -1))) u |= 0x01;
|
||||
if (!s.IsVisible(p + new CVec(1, -1))) u |= 0x02;
|
||||
if (!s.IsVisible(p + new CVec(1, 1))) u |= 0x04;
|
||||
if (!s.IsVisible(p + new CVec(-1, 1))) u |= 0x08;
|
||||
|
||||
// RA provides a set of frames for tiles with shrouded
|
||||
// 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)
|
||||
{
|
||||
if (!s.IsExplored(p.X, p.Y))
|
||||
if (!s.IsExplored(p))
|
||||
return 15;
|
||||
|
||||
// If a side is shrouded then we also count the corners
|
||||
var u = 0;
|
||||
if (!s.IsExplored(p.X, p.Y - 1)) u |= 0x13;
|
||||
if (!s.IsExplored(p.X + 1, p.Y)) u |= 0x26;
|
||||
if (!s.IsExplored(p.X, p.Y + 1)) u |= 0x4C;
|
||||
if (!s.IsExplored(p.X - 1, p.Y)) u |= 0x89;
|
||||
if (!s.IsExplored(p + new CVec(0, -1))) u |= 0x13;
|
||||
if (!s.IsExplored(p + new CVec(1, 0))) u |= 0x26;
|
||||
if (!s.IsExplored(p + new CVec(0, 1))) u |= 0x4C;
|
||||
if (!s.IsExplored(p + new CVec(-1, 0))) u |= 0x89;
|
||||
|
||||
var uside = u & 0x0F;
|
||||
if (!s.IsExplored(p.X - 1, p.Y - 1)) u |= 0x01;
|
||||
if (!s.IsExplored(p.X + 1, p.Y - 1)) u |= 0x02;
|
||||
if (!s.IsExplored(p.X + 1, p.Y + 1)) u |= 0x04;
|
||||
if (!s.IsExplored(p.X - 1, p.Y + 1)) u |= 0x08;
|
||||
if (!s.IsExplored(p + new CVec(-1, -1))) u |= 0x01;
|
||||
if (!s.IsExplored(p + new CVec(1, -1))) u |= 0x02;
|
||||
if (!s.IsExplored(p + new CVec(1, 1))) u |= 0x04;
|
||||
if (!s.IsExplored(p + new CVec(-1, 1))) u |= 0x08;
|
||||
|
||||
// RA provides a set of frames for tiles with shrouded
|
||||
// corners but unshrouded edges. We want to detect this
|
||||
@@ -173,15 +175,12 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
// Cache the tile positions to avoid unnecessary calculations
|
||||
for (var i = bounds.Left; i < bounds.Right; i++)
|
||||
// Initialize tile cache
|
||||
foreach (var cell in map.Cells)
|
||||
{
|
||||
for (var j = bounds.Top; j < bounds.Bottom; j++)
|
||||
{
|
||||
var k = j * tileStride + i;
|
||||
tiles[k].Position = new CPos(i, j);
|
||||
tiles[k].ScreenPosition = wr.ScreenPosition(tiles[k].Position.CenterPosition);
|
||||
}
|
||||
var screen = wr.ScreenPosition(cell.CenterPosition);
|
||||
var variant = Game.CosmeticRandom.Next(info.Variants.Length);
|
||||
tiles[cell] = new ShroudTile(cell, screen, variant);
|
||||
}
|
||||
|
||||
fogPalette = wr.Palette("fog");
|
||||
@@ -209,22 +208,25 @@ namespace OpenRA.Mods.RA
|
||||
if (shroud == null)
|
||||
{
|
||||
// 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);
|
||||
tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant);
|
||||
tiles[k].Fog = GetTile(shrouded, tiles[k].Variant);
|
||||
var t = tiles[cell];
|
||||
var shrouded = ObserverShroudedEdges(t.Position, map.Bounds, info.UseExtendedIndex);
|
||||
|
||||
t.Shroud = GetTile(shrouded, t.Variant);
|
||||
t.Fog = GetTile(shrouded, t.Variant);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var k = 0; k < tiles.Length; k++)
|
||||
foreach (var cell in map.Cells)
|
||||
{
|
||||
var shrouded = ShroudedEdges(shroud, tiles[k].Position, useExtendedIndex);
|
||||
var fogged = FoggedEdges(shroud, tiles[k].Position, useExtendedIndex);
|
||||
var t = tiles[cell];
|
||||
var shrouded = ShroudedEdges(shroud, t.Position, info.UseExtendedIndex);
|
||||
var fogged = FoggedEdges(shroud, t.Position, info.UseExtendedIndex);
|
||||
|
||||
tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant);
|
||||
tiles[k].Fog = GetTile(fogged, tiles[k].Variant);
|
||||
t.Shroud = GetTile(shrouded, t.Variant);
|
||||
t.Fog = GetTile(fogged, t.Variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,27 +235,20 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
Update(shroud);
|
||||
|
||||
var clip = wr.Viewport.CellBounds;
|
||||
var width = clip.Width;
|
||||
for (var j = clip.Top; j < clip.Bottom; j++)
|
||||
foreach (var cell in wr.Viewport.VisibleCells)
|
||||
{
|
||||
var start = j * tileStride + clip.Left;
|
||||
for (var k = 0; k < width; k++)
|
||||
var t = tiles[cell];
|
||||
|
||||
if (t.Shroud != null)
|
||||
{
|
||||
var s = tiles[start + k].Shroud;
|
||||
var f = tiles[start + k].Fog;
|
||||
var pos = t.ScreenPosition - 0.5f * t.Shroud.size;
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Shroud, pos, shroudPalette);
|
||||
}
|
||||
|
||||
if (s != null)
|
||||
{
|
||||
var pos = tiles[start + k].ScreenPosition - 0.5f * s.size;
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(s, pos, shroudPalette);
|
||||
}
|
||||
|
||||
if (f != null)
|
||||
{
|
||||
var pos = tiles[start + k].ScreenPosition - 0.5f * f.size;
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(f, pos, fogPalette);
|
||||
}
|
||||
if (t.Fog != null)
|
||||
{
|
||||
var pos = t.ScreenPosition - 0.5f * t.Fog.size;
|
||||
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)
|
||||
{
|
||||
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 };
|
||||
}
|
||||
|
||||
@@ -258,6 +258,6 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
||||
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>();
|
||||
}
|
||||
|
||||
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
|
||||
var s = theater.TileSprite(tile);
|
||||
@@ -59,12 +59,11 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var cliprect = wr.Viewport.CellBounds;
|
||||
var pal = wr.Palette("terrain");
|
||||
|
||||
foreach (var kv in tiles)
|
||||
{
|
||||
if (!cliprect.Contains(kv.Key.X, kv.Key.Y))
|
||||
if (!wr.Viewport.VisibleCells.Contains(kv.Key))
|
||||
continue;
|
||||
|
||||
if (wr.world.ShroudObscures(kv.Key))
|
||||
|
||||
@@ -53,17 +53,17 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
class MovementClassDomainIndex
|
||||
{
|
||||
Rectangle bounds;
|
||||
Map map;
|
||||
|
||||
uint movementClass;
|
||||
int[,] domains;
|
||||
CellLayer<int> domains;
|
||||
Dictionary<int, HashSet<int>> transientConnections;
|
||||
|
||||
public MovementClassDomainIndex(World world, uint movementClass)
|
||||
{
|
||||
bounds = world.Map.Bounds;
|
||||
map = world.Map;
|
||||
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>>();
|
||||
|
||||
BuildDomains(world);
|
||||
@@ -71,15 +71,15 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
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;
|
||||
|
||||
if (domains[p1.X, p1.Y] == domains[p2.X, p2.Y])
|
||||
if (domains[p1] == domains[p2])
|
||||
return true;
|
||||
|
||||
// Even though p1 and p2 are in different domains, it's possible
|
||||
// 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)
|
||||
@@ -90,22 +90,21 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
// Select all neighbors inside the map boundries
|
||||
var neighbors = CVec.directions.Select(d => d + cell)
|
||||
.Where(c => bounds.Contains(c.X, c.Y));
|
||||
.Where(c => map.Contains(c));
|
||||
|
||||
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 match = CanTraverseTile(world, neighbor);
|
||||
if (match) neighborDomains.Add(neighborDomain);
|
||||
var neighborDomain = domains[n];
|
||||
if (CanTraverseTile(world, n))
|
||||
neighborDomains.Add(neighborDomain);
|
||||
|
||||
// Set ourselves to the first non-dirty neighbor we find.
|
||||
if (!found)
|
||||
{
|
||||
SetDomain(cell, neighborDomain);
|
||||
domains[cell] = neighborDomain;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@@ -113,20 +112,8 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
|
||||
foreach (var c1 in neighborDomains)
|
||||
{
|
||||
foreach (var c2 in neighborDomains)
|
||||
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)
|
||||
@@ -146,6 +133,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
if (neighbor == d2)
|
||||
return true;
|
||||
|
||||
if (!visited.Contains(neighbor))
|
||||
toProcess.Push(neighbor);
|
||||
}
|
||||
@@ -180,7 +168,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
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>();
|
||||
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
|
||||
// ballooning the domain counter.
|
||||
if (visited[start.X, start.Y])
|
||||
if (visited[start])
|
||||
continue;
|
||||
|
||||
var domainQueue = new Queue<CPos>();
|
||||
@@ -205,7 +193,7 @@ namespace OpenRA.Mods.RA
|
||||
while (domainQueue.Count != 0)
|
||||
{
|
||||
var n = domainQueue.Dequeue();
|
||||
if (visited[n.X, n.Y])
|
||||
if (visited[n])
|
||||
continue;
|
||||
|
||||
var candidatePassable = CanTraverseTile(world, n);
|
||||
@@ -215,12 +203,12 @@ namespace OpenRA.Mods.RA
|
||||
continue;
|
||||
}
|
||||
|
||||
visited[n.X, n.Y] = true;
|
||||
SetDomain(n, domain);
|
||||
visited[n] = true;
|
||||
domains[n] = domain;
|
||||
|
||||
// Don't crawl off the map, or add already-visited cells
|
||||
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)
|
||||
domainQueue.Enqueue(neighbor);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
@@ -20,7 +21,7 @@ namespace OpenRA.Mods.RA
|
||||
class PathfinderDebugOverlayInfo : TraitInfo<PathfinderDebugOverlay> { }
|
||||
class PathfinderDebugOverlay : IRenderOverlay, IWorldLoaded
|
||||
{
|
||||
Dictionary<Player, int[,]> layers;
|
||||
Dictionary<Player, CellLayer<int>> layers;
|
||||
int refreshTick;
|
||||
World world;
|
||||
public bool Visible;
|
||||
@@ -29,7 +30,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
world = w;
|
||||
refreshTick = 0;
|
||||
layers = new Dictionary<Player, int[,]>(8);
|
||||
layers = new Dictionary<Player, CellLayer<int>>(8);
|
||||
|
||||
// Enabled via Cheats menu
|
||||
Visible = false;
|
||||
@@ -39,15 +40,15 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
if (maxWeight == 0) return;
|
||||
|
||||
int[,] layer;
|
||||
CellLayer<int> 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);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -59,29 +60,25 @@ namespace OpenRA.Mods.RA
|
||||
var doDim = refreshTick - world.WorldTick <= 0;
|
||||
if (doDim) refreshTick = world.WorldTick + 20;
|
||||
|
||||
var viewBounds = wr.Viewport.CellBounds;
|
||||
foreach (var pair in layers)
|
||||
{
|
||||
var c = (pair.Key != null) ? pair.Key.Color.RGB : Color.PaleTurquoise;
|
||||
var layer = pair.Value;
|
||||
|
||||
// Only render quads in viewing range:
|
||||
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[i, j] <= 0)
|
||||
continue;
|
||||
if (layer[cell] <= 0)
|
||||
continue;
|
||||
|
||||
var w = Math.Max(0, Math.Min(layer[i, j], 128));
|
||||
if (doDim)
|
||||
layer[i, j] = layer[i, j] * 5 / 6;
|
||||
var w = Math.Max(0, Math.Min(layer[cell], 128));
|
||||
if (doDim)
|
||||
layer[cell] = layer[cell] * 5 / 6;
|
||||
|
||||
// TODO: This doesn't make sense for isometric terrain
|
||||
var tl = wr.ScreenPxPosition(new CPos(i, j).TopLeft);
|
||||
var br = wr.ScreenPxPosition(new CPos(i, j).BottomRight);
|
||||
qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c));
|
||||
}
|
||||
// TODO: This doesn't make sense for isometric terrain
|
||||
var tl = wr.ScreenPxPosition(cell.TopLeft);
|
||||
var br = wr.ScreenPxPosition(cell.BottomRight);
|
||||
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)
|
||||
{
|
||||
var cliprect = wr.Viewport.CellBounds;
|
||||
var pal = wr.Palette("terrain");
|
||||
|
||||
foreach (var kv in tiles)
|
||||
{
|
||||
if (!cliprect.Contains(kv.Key.X, kv.Key.Y))
|
||||
if (!wr.Viewport.VisibleCells.Contains(kv.Key))
|
||||
continue;
|
||||
|
||||
if (world.ShroudObscures(kv.Key))
|
||||
|
||||
@@ -137,6 +137,7 @@ namespace OpenRA.Utility
|
||||
var width = Exts.ParseIntegerInvariant(mapSection.GetValue("Width", "0"));
|
||||
var height = Exts.ParseIntegerInvariant(mapSection.GetValue("Height", "0"));
|
||||
mapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64;
|
||||
var size = new Size(mapSize, mapSize);
|
||||
|
||||
map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(iniFile));
|
||||
map.Author = "Westwood Studios";
|
||||
@@ -148,8 +149,8 @@ namespace OpenRA.Utility
|
||||
|
||||
map.Smudges = Exts.Lazy(() => new List<SmudgeReference>());
|
||||
map.Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>());
|
||||
map.MapResources = Exts.Lazy(() => new TileReference<byte, byte>[mapSize, mapSize]);
|
||||
map.MapTiles = Exts.Lazy(() => new TileReference<ushort, byte>[mapSize, mapSize]);
|
||||
map.MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(size));
|
||||
map.MapTiles = Exts.Lazy(() => new CellLayer<TerrainTile>(size));
|
||||
|
||||
map.Options = new MapOptions();
|
||||
|
||||
@@ -250,20 +251,20 @@ namespace OpenRA.Utility
|
||||
|
||||
void UnpackRATileData(MemoryStream ms)
|
||||
{
|
||||
for (var i = 0; i < mapSize; i++)
|
||||
for (var j = 0; j < mapSize; j++)
|
||||
map.MapTiles.Value[i, j] = new TileReference<ushort, byte>();
|
||||
|
||||
var types = new ushort[mapSize, mapSize];
|
||||
for (var j = 0; j < mapSize; j++)
|
||||
{
|
||||
for (var i = 0; i < mapSize; i++)
|
||||
{
|
||||
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 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)
|
||||
@@ -277,15 +278,16 @@ namespace OpenRA.Utility
|
||||
|
||||
if (o != 255 && overlayResourceMapping.ContainsKey(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]))
|
||||
{
|
||||
map.Actors.Value.Add("Actor" + actorCount++,
|
||||
new ActorReference(overlayActorMapping[redAlertOverlayNames[o]])
|
||||
{
|
||||
new LocationInit(new CPos(i, j)),
|
||||
new LocationInit(cell),
|
||||
new OwnerInit("Neutral")
|
||||
});
|
||||
}
|
||||
@@ -313,16 +315,13 @@ namespace OpenRA.Utility
|
||||
|
||||
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 i = 0; i < mapSize; i++)
|
||||
{
|
||||
map.MapTiles.Value[i, j].Type = ms.ReadUInt8();
|
||||
map.MapTiles.Value[i, j].Index = ms.ReadUInt8();
|
||||
var type = 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()))
|
||||
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()))
|
||||
map.Actors.Value.Add("Actor" + actorCount++,
|
||||
|
||||
Reference in New Issue
Block a user