Implement isometric selection boxes for TS structures.
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Graphics
|
||||
{
|
||||
public struct IsometricSelectionBarsAnnotationRenderable : IRenderable, IFinalizedRenderable
|
||||
{
|
||||
const int BarWidth = 3;
|
||||
const int BarHeight = 4;
|
||||
const int BarStride = 5;
|
||||
|
||||
static readonly Color EmptyColor = Color.FromArgb(160, 30, 30, 30);
|
||||
static readonly Color DarkEmptyColor = Color.FromArgb(160, 15, 15, 15);
|
||||
static readonly Color DarkenColor = Color.FromArgb(24, 0, 0, 0);
|
||||
static readonly Color LightenColor = Color.FromArgb(24, 255, 255, 255);
|
||||
|
||||
readonly WPos pos;
|
||||
readonly Actor actor;
|
||||
readonly bool displayHealth;
|
||||
readonly bool displayExtra;
|
||||
readonly Polygon bounds;
|
||||
|
||||
public IsometricSelectionBarsAnnotationRenderable(Actor actor, Polygon bounds, bool displayHealth, bool displayExtra)
|
||||
: this(actor.CenterPosition, actor, bounds)
|
||||
{
|
||||
this.displayHealth = displayHealth;
|
||||
this.displayExtra = displayExtra;
|
||||
}
|
||||
|
||||
public IsometricSelectionBarsAnnotationRenderable(WPos pos, Actor actor, Polygon bounds)
|
||||
: this()
|
||||
{
|
||||
this.pos = pos;
|
||||
this.actor = actor;
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos; } }
|
||||
public bool DisplayHealth { get { return displayHealth; } }
|
||||
public bool DisplayExtra { get { return displayExtra; } }
|
||||
|
||||
public PaletteReference Palette { get { return null; } }
|
||||
public int ZOffset { get { return 0; } }
|
||||
public bool IsDecoration { get { return true; } }
|
||||
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
|
||||
public IRenderable WithZOffset(int newOffset) { return this; }
|
||||
public IRenderable OffsetBy(WVec vec) { return new IsometricSelectionBarsAnnotationRenderable(pos + vec, actor, bounds); }
|
||||
public IRenderable AsDecoration() { return this; }
|
||||
|
||||
void DrawExtraBars(WorldRenderer wr)
|
||||
{
|
||||
var i = 1;
|
||||
foreach (var extraBar in actor.TraitsImplementing<ISelectionBar>())
|
||||
{
|
||||
var value = extraBar.GetValue();
|
||||
if (value != 0 || extraBar.DisplayWhenEmpty)
|
||||
DrawBar(wr, extraBar.GetValue(), extraBar.GetColor(), i++);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBar(WorldRenderer wr, float value, Color barColor, int barNum, float? secondValue = null, Color? secondColor = null)
|
||||
{
|
||||
var darkColor = Color.FromArgb(barColor.A, barColor.R / 2, barColor.G / 2, barColor.B / 2);
|
||||
var barAspect = new float2(1f, 0.5f);
|
||||
var stepAspect = new float2(1f, -0.5f);
|
||||
|
||||
var offset = barNum * BarStride * barAspect - new float2(0, BarHeight + 1);
|
||||
var start = wr.Viewport.WorldToViewPx(bounds.Vertices[1]).ToFloat2() + offset;
|
||||
var end = wr.Viewport.WorldToViewPx(bounds.Vertices[0]).ToFloat2() + offset;
|
||||
|
||||
// HACK: Work around rounding errors that may cause a few-px offset in the end relative to the start
|
||||
// Force the bar to take a 45 degree angle
|
||||
end = new float2(end.X, start.Y - (end.X - start.X) / 2);
|
||||
|
||||
// Round the cut point to the nearest pixel to avoid potential off-by-one pixel offsets distorting the bar
|
||||
var cutX = (int)(float2.Lerp(start.X, end.X, value) + 0.5f);
|
||||
var cut = new float2(cutX, start.Y - (cutX - start.X) / 2);
|
||||
|
||||
var cr = Game.Renderer.RgbaColorRenderer;
|
||||
var da = BarWidth * barAspect;
|
||||
var db = new int2(0, BarHeight);
|
||||
var dc = da + db;
|
||||
|
||||
// Filled bar
|
||||
cr.FillRect(start + da, start + dc, cut + dc, cut + da, darkColor);
|
||||
cr.FillRect(start, start + da, start + dc, start + db, darkColor);
|
||||
cr.FillRect(start, start + da, cut + da, cut, barColor);
|
||||
|
||||
// Faint marks to break the monotony of the solid bar
|
||||
var dx = BarWidth;
|
||||
while (dx < cut.X - start.X)
|
||||
{
|
||||
var step = start + dx * stepAspect;
|
||||
cr.DrawLine(step, step + da, 1, DarkenColor);
|
||||
cr.DrawLine(step + da, step + dc, 1, LightenColor);
|
||||
dx += BarWidth;
|
||||
}
|
||||
|
||||
// Second bar (e.g. applied damage)
|
||||
if (secondValue.HasValue && secondColor.HasValue)
|
||||
{
|
||||
var secondCutX = (int)(float2.Lerp(start.X, end.X, secondValue.Value) + 0.5f);
|
||||
var secondCut = new float2(secondCutX, start.Y - (secondCutX - start.X) / 2);
|
||||
var darkSecond = Color.FromArgb(secondColor.Value.A, secondColor.Value.R / 2, secondColor.Value.G / 2, secondColor.Value.B / 2);
|
||||
|
||||
cr.FillRect(cut + da, cut + dc, secondCut + dc, secondCut + da, darkSecond);
|
||||
cr.FillRect(cut, cut + da, secondCut + da, secondCut, secondColor.Value);
|
||||
|
||||
value = secondValue.Value;
|
||||
cut = secondCut;
|
||||
}
|
||||
|
||||
// Empty bar
|
||||
if (value < 1)
|
||||
{
|
||||
cr.FillRect(cut + da, cut + dc, end + dc, end + da, DarkEmptyColor);
|
||||
cr.FillRect(cut, cut + da, end + da, end, EmptyColor);
|
||||
}
|
||||
}
|
||||
|
||||
Color GetHealthColor(IHealth health)
|
||||
{
|
||||
if (Game.Settings.Game.UsePlayerStanceColors)
|
||||
return actor.Owner.PlayerStanceColor(actor);
|
||||
|
||||
return health.DamageState == DamageState.Critical ? Color.Red :
|
||||
health.DamageState == DamageState.Heavy ? Color.Yellow : Color.LimeGreen;
|
||||
}
|
||||
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
if (!actor.IsInWorld || actor.IsDead)
|
||||
return;
|
||||
|
||||
var health = actor.TraitOrDefault<IHealth>();
|
||||
|
||||
if (DisplayHealth)
|
||||
{
|
||||
if (health == null || health.IsDead)
|
||||
return;
|
||||
|
||||
var displayValue = health.DisplayHP != health.HP ? (float?)health.DisplayHP / health.MaxHP : null;
|
||||
DrawBar(wr, (float)health.HP / health.MaxHP, GetHealthColor(health), 0, displayValue, Color.OrangeRed);
|
||||
}
|
||||
|
||||
if (DisplayExtra)
|
||||
DrawExtraBars(wr);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr) { }
|
||||
public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.Common.Graphics
|
||||
{
|
||||
public struct IsometricSelectionBoxAnnotationRenderable : IRenderable, IFinalizedRenderable
|
||||
{
|
||||
static readonly float2 TLOffset = new float2(-12, -6);
|
||||
static readonly float2 TROffset = new float2(12, -6);
|
||||
static readonly float2 TOffset = new float2(0, -13);
|
||||
static readonly float2[] Offsets =
|
||||
{
|
||||
-TROffset, -TLOffset, -TOffset,
|
||||
TROffset, -TOffset, -TLOffset,
|
||||
-TLOffset, TOffset, TROffset,
|
||||
TLOffset, TROffset, TOffset,
|
||||
-TROffset, TOffset, TLOffset,
|
||||
TLOffset, -TOffset, -TROffset
|
||||
};
|
||||
|
||||
readonly WPos pos;
|
||||
readonly Polygon bounds;
|
||||
readonly Color color;
|
||||
|
||||
public IsometricSelectionBoxAnnotationRenderable(Actor actor, Polygon bounds, Color color)
|
||||
{
|
||||
pos = actor.CenterPosition;
|
||||
this.bounds = bounds;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public IsometricSelectionBoxAnnotationRenderable(WPos pos, Polygon bounds, Color color)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.bounds = bounds;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos; } }
|
||||
|
||||
public PaletteReference Palette { get { return null; } }
|
||||
public int ZOffset { get { return 0; } }
|
||||
public bool IsDecoration { get { return true; } }
|
||||
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
|
||||
public IRenderable WithZOffset(int newOffset) { return this; }
|
||||
public IRenderable OffsetBy(WVec vec) { return new IsometricSelectionBoxAnnotationRenderable(pos + vec, bounds, color); }
|
||||
public IRenderable AsDecoration() { return this; }
|
||||
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var screen = bounds.Vertices.Select(v => wr.Viewport.WorldToViewPx(v).ToFloat2()).ToArray();
|
||||
|
||||
var tl = new float2(-12, -6);
|
||||
var tr = new float2(12, -6);
|
||||
var t = new float2(0, -13);
|
||||
|
||||
var cr = Game.Renderer.RgbaColorRenderer;
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
cr.DrawLine(new float3[] { screen[i] + Offsets[3 * i], screen[i], screen[i] + Offsets[3 * i + 1] }, 1, color, true);
|
||||
cr.DrawLine(new float3[] { screen[i], screen[i] + Offsets[3 * i + 2] }, 1, color, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr) { }
|
||||
public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
|
||||
}
|
||||
}
|
||||
142
OpenRA.Mods.Common/Traits/IsometricSelectable.cs
Normal file
142
OpenRA.Mods.Common/Traits/IsometricSelectable.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("This actor is selectable. Defines bounds of selectable area, selection class, selection priority and selection priority modifiers.")]
|
||||
public class IsometricSelectableInfo : ITraitInfo, IMouseBoundsInfo, ISelectableInfo, IRulesetLoaded, Requires<BuildingInfo>
|
||||
{
|
||||
[Desc("Defines a custom rectangle for mouse interaction with the actor.",
|
||||
"If null, the engine will guess an appropriate size based on the building's footprint.",
|
||||
"The first two numbers define the width and depth of the footprint rectangle.",
|
||||
"The (optional) second two numbers define an x and y offset from the actor center.")]
|
||||
public readonly int[] Bounds = null;
|
||||
|
||||
[Desc("Height above the footprint for the top of the interaction rectangle.")]
|
||||
public readonly int Height = 24;
|
||||
|
||||
[Desc("Defines a custom rectangle for Decorations (e.g. the selection box).",
|
||||
"If null, Bounds will be used instead.")]
|
||||
public readonly int[] DecorationBounds = null;
|
||||
|
||||
[Desc("Defines a custom height for Decorations (e.g. the selection box).",
|
||||
"If < 0, Height will be used instead.",
|
||||
"If Height is 0, this must be defined with a value greater than 0.")]
|
||||
public readonly int DecorationHeight = -1;
|
||||
|
||||
public readonly int Priority = 10;
|
||||
|
||||
[Desc("Allow selection priority to be modified using a hotkey.",
|
||||
"Valid values are None (priority is not affected by modifiers)",
|
||||
"Ctrl (priority is raised when Ctrl pressed) and",
|
||||
"Alt (priority is raised when Alt pressed).")]
|
||||
public readonly SelectionPriorityModifiers PriorityModifiers = SelectionPriorityModifiers.None;
|
||||
|
||||
[Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). ",
|
||||
"Defaults to the actor name when not defined or inherited.")]
|
||||
public readonly string Class = null;
|
||||
|
||||
[VoiceReference]
|
||||
public readonly string Voice = "Select";
|
||||
|
||||
public object Create(ActorInitializer init) { return new IsometricSelectable(init.Self, this); }
|
||||
|
||||
int ISelectableInfo.Priority { get { return Priority; } }
|
||||
SelectionPriorityModifiers ISelectableInfo.PriorityModifiers { get { return PriorityModifiers; } }
|
||||
string ISelectableInfo.Voice { get { return Voice; } }
|
||||
|
||||
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
||||
{
|
||||
var grid = Game.ModData.Manifest.Get<MapGrid>();
|
||||
if (grid.Type != MapGridType.RectangularIsometric)
|
||||
throw new YamlException("IsometricSelectable can only be used in mods that use the RectangularIsometric MapGrid type.");
|
||||
|
||||
if (Height == 0 && DecorationHeight <= 0)
|
||||
throw new YamlException("DecorationHeight must be defined and greater than 0 if Height is 0.");
|
||||
}
|
||||
}
|
||||
|
||||
public class IsometricSelectable : IMouseBounds, ISelectable
|
||||
{
|
||||
readonly IsometricSelectableInfo info;
|
||||
readonly string selectionClass = null;
|
||||
readonly BuildingInfo buildingInfo;
|
||||
|
||||
public IsometricSelectable(Actor self, IsometricSelectableInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
selectionClass = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
|
||||
buildingInfo = self.Info.TraitInfo<BuildingInfo>();
|
||||
}
|
||||
|
||||
Polygon Bounds(Actor self, WorldRenderer wr, int[] bounds, int height)
|
||||
{
|
||||
int2 left, right, top, bottom;
|
||||
if (bounds != null)
|
||||
{
|
||||
var offset = bounds.Length >= 4 ? new int2(bounds[2], bounds[3]) : int2.Zero;
|
||||
var center = wr.ScreenPxPosition(self.CenterPosition) + offset;
|
||||
left = center - new int2(bounds[0] / 2, 0);
|
||||
right = left + new int2(bounds[0], 0);
|
||||
top = center - new int2(0, bounds[1] / 2);
|
||||
bottom = top + new int2(0, bounds[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var xMin = int.MaxValue;
|
||||
var xMax = int.MinValue;
|
||||
var yMin = int.MaxValue;
|
||||
var yMax = int.MinValue;
|
||||
foreach (var c in buildingInfo.OccupiedTiles(self.Location))
|
||||
{
|
||||
xMin = Math.Min(xMin, c.X);
|
||||
xMax = Math.Max(xMax, c.X);
|
||||
yMin = Math.Min(yMin, c.Y);
|
||||
yMax = Math.Max(yMax, c.Y);
|
||||
}
|
||||
|
||||
left = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMin, yMax)) - new WVec(768, 0, 0));
|
||||
right = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMax, yMin)) + new WVec(768, 0, 0));
|
||||
top = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMin, yMin)) - new WVec(0, 768, 0));
|
||||
bottom = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMax, yMax)) + new WVec(0, 768, 0));
|
||||
}
|
||||
|
||||
if (height == 0)
|
||||
return new Polygon(new[] { top, left, bottom, right });
|
||||
|
||||
var h = new int2(0, height);
|
||||
return new Polygon(new[] { top - h, left - h, left, bottom, right, right - h });
|
||||
}
|
||||
|
||||
public Polygon Bounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Bounds(self, wr, info.Bounds, info.Height);
|
||||
}
|
||||
|
||||
public Polygon DecorationBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Bounds(self, wr, info.DecorationBounds ?? info.Bounds, info.DecorationHeight >= 0 ? info.DecorationHeight : info.Height);
|
||||
}
|
||||
|
||||
Polygon IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Bounds(self, wr);
|
||||
}
|
||||
|
||||
string ISelectable.Class { get { return selectionClass; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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, 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.Mods.Common.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
public class IsometricSelectionDecorationsInfo : SelectionDecorationsBaseInfo, Requires<IsometricSelectableInfo>
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new IsometricSelectionDecorations(init.Self, this); }
|
||||
}
|
||||
|
||||
public class IsometricSelectionDecorations : SelectionDecorationsBase
|
||||
{
|
||||
readonly IsometricSelectable selectable;
|
||||
|
||||
public IsometricSelectionDecorations(Actor self, IsometricSelectionDecorationsInfo info)
|
||||
: base(info)
|
||||
{
|
||||
selectable = self.Trait<IsometricSelectable>();
|
||||
}
|
||||
|
||||
protected override int2 GetDecorationPosition(Actor self, WorldRenderer wr, DecorationPosition pos)
|
||||
{
|
||||
var bounds = selectable.DecorationBounds(self, wr);
|
||||
switch (pos)
|
||||
{
|
||||
case DecorationPosition.TopLeft: return bounds.Vertices[1];
|
||||
case DecorationPosition.TopRight: return bounds.Vertices[5];
|
||||
case DecorationPosition.BottomLeft: return bounds.Vertices[2];
|
||||
case DecorationPosition.BottomRight: return bounds.Vertices[4];
|
||||
case DecorationPosition.Top: return new int2((bounds.Vertices[1].X + bounds.Vertices[5].X) / 2, bounds.Vertices[1].Y);
|
||||
default: return bounds.BoundingRect.TopLeft + new int2(bounds.BoundingRect.Size.Width / 2, bounds.BoundingRect.Size.Height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<IRenderable> RenderSelectionBox(Actor self, WorldRenderer wr, Color color)
|
||||
{
|
||||
var bounds = selectable.DecorationBounds(self, wr);
|
||||
yield return new IsometricSelectionBoxAnnotationRenderable(self, bounds, color);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IRenderable> RenderSelectionBars(Actor self, WorldRenderer wr, bool displayHealth, bool displayExtra)
|
||||
{
|
||||
if (!displayHealth && !displayExtra)
|
||||
yield break;
|
||||
|
||||
var bounds = selectable.DecorationBounds(self, wr);
|
||||
yield return new IsometricSelectionBarsAnnotationRenderable(self, bounds, displayHealth, displayExtra);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.FileFormats;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||
{
|
||||
public class CopyIsometricSelectableHeight : UpdateRule
|
||||
{
|
||||
public override string Name { get { return "Copy IsometricSelectable.Height from art*.ini definitions."; } }
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Reads building Height entries art.ini/artfs.ini/artmd.ini from the current working directory\n" +
|
||||
"and adds IsometricSelectable definitions to matching actors.";
|
||||
}
|
||||
}
|
||||
|
||||
static readonly string[] SourceFiles = { "art.ini", "artfs.ini", "artmd.ini" };
|
||||
|
||||
readonly Dictionary<string, int> selectionHeight = new Dictionary<string, int>();
|
||||
|
||||
bool complete;
|
||||
|
||||
public override IEnumerable<string> BeforeUpdate(ModData modData)
|
||||
{
|
||||
if (complete)
|
||||
yield break;
|
||||
|
||||
var grid = Game.ModData.Manifest.Get<MapGrid>();
|
||||
foreach (var filename in SourceFiles)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
continue;
|
||||
|
||||
var file = new IniFile(File.Open(filename, FileMode.Open));
|
||||
foreach (var section in file.Sections)
|
||||
{
|
||||
if (!section.Contains("Height"))
|
||||
continue;
|
||||
|
||||
selectionHeight[section.Name] = (int)(float.Parse(section.GetValue("Height", "1")) * grid.TileSize.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||
{
|
||||
// Rule only applies to the default ruleset - skip maps
|
||||
complete = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
{
|
||||
if (complete || actorNode.LastChildMatching("IsometricSelectable") != null)
|
||||
yield break;
|
||||
|
||||
var height = 0;
|
||||
if (!selectionHeight.TryGetValue(actorNode.Key.ToLowerInvariant(), out height))
|
||||
yield break;
|
||||
|
||||
// Don't redefine the default value
|
||||
if (height == 24)
|
||||
yield break;
|
||||
|
||||
var selection = new MiniYamlNode("IsometricSelectable", "");
|
||||
selection.AddNode("Height", FieldSaver.FormatValue(height));
|
||||
|
||||
actorNode.AddNode(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user