Files
OpenRA/OpenRA.Mods.Common/Graphics/BorderedRegionRenderable.cs
2024-08-03 13:27:18 +03:00

99 lines
3.6 KiB
C#

#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics
{
public readonly struct BorderedRegionRenderable : IRenderable, IFinalizedRenderable
{
enum Corner { TopLeft, TopRight, BottomRight, BottomLeft }
// Maps a cell offset to the index of the corner (in the 'Corner' arrays in the MapGrid.CellRamp structs)
// from which a border should be drawn. The index of the end corner will be (cornerIndex + 1) % 4.
static readonly Dictionary<CVec, int> Offset2CornerIndex = new()
{
{ new CVec(0, -1), (int)Corner.TopLeft },
{ new CVec(1, 0), (int)Corner.TopRight },
{ new CVec(0, 1), (int)Corner.BottomRight },
{ new CVec(-1, 0), (int)Corner.BottomLeft },
};
readonly CPos[] region;
readonly Color color, contrastColor;
readonly float width, contrastWidth;
public BorderedRegionRenderable(CPos[] region, Color color, float width, Color contrastColor, float contrastWidth)
{
this.region = region;
this.color = color;
this.contrastColor = contrastColor;
this.width = width;
this.contrastWidth = contrastWidth;
}
readonly WPos IRenderable.Pos { get { return WPos.Zero; } }
readonly int IRenderable.ZOffset { get { return 0; } }
readonly bool IRenderable.IsDecoration { get { return true; } }
IRenderable IRenderable.WithZOffset(int newOffset) { return new BorderedRegionRenderable(region, color, width, contrastColor, contrastWidth); }
IRenderable IRenderable.OffsetBy(in WVec offset) { return new BorderedRegionRenderable(region, color, width, contrastColor, contrastWidth); }
IRenderable IRenderable.AsDecoration() { return this; }
IFinalizedRenderable IRenderable.PrepareRender(WorldRenderer wr) { return this; }
void IFinalizedRenderable.Render(WorldRenderer wr) { Draw(wr, region, color, width, contrastColor, contrastWidth); }
void IFinalizedRenderable.RenderDebugGeometry(WorldRenderer wr) { }
Rectangle IFinalizedRenderable.ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
public static void Draw(WorldRenderer wr, CPos[] region, Color color, float width, Color constrastColor, float constrastWidth)
{
if (width == 0 && constrastWidth == 0)
return;
var map = wr.World.Map;
var cr = Game.Renderer.RgbaColorRenderer;
foreach (var c in region)
{
var mpos = c.ToMPos(map);
if (!map.Height.Contains(mpos) || wr.World.ShroudObscures(c))
continue;
var tile = map.Tiles[mpos];
var ti = map.Rules.TerrainInfo.GetTerrainInfo(tile);
var ramp = ti?.RampType ?? 0;
var corners = map.Grid.Ramps[ramp].Corners;
var pos = map.CenterOfCell(c) - new WVec(0, 0, map.Grid.Ramps[ramp].CenterHeightOffset);
foreach (var o in Offset2CornerIndex)
{
// If the neighboring cell is part of the region, don't draw a border between the cells.
if (region.Contains(c + o.Key))
continue;
var start = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos + corners[o.Value]));
var end = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos + corners[(o.Value + 1) % 4]));
if (constrastWidth > 0)
cr.DrawLine(start, end, constrastWidth, constrastColor);
if (width > 0)
cr.DrawLine(start, end, width, color);
}
}
}
}
}