diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index a517a6c0c3..d554f57d94 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -474,6 +474,8 @@
+
+
diff --git a/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs b/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs
index 9524e8cc33..5fe350c477 100644
--- a/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs
+++ b/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs
@@ -36,9 +36,6 @@ namespace OpenRA.Mods.Common.Traits.Render
public readonly string Image = "pips";
- [Desc("Sprite sequence used to render the control group 0-9 numbers.")]
- [SequenceReference("Image")] public readonly string GroupSequence = "groups";
-
public object Create(ActorInitializer init) { return new SelectionDecorations(init.Self, this); }
public int[] SelectionBoxBounds { get { return VisualBounds; } }
@@ -97,29 +94,13 @@ namespace OpenRA.Mods.Common.Traits.Render
var b = self.VisualBounds;
var pos = wr.ScreenPxPosition(self.CenterPosition);
- var tl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Top));
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
var pal = wr.Palette(Info.Palette);
- foreach (var r in DrawControlGroup(wr, self, tl, pal))
- yield return r;
-
foreach (var r in DrawPips(wr, self, bl, pal))
yield return r;
}
- IEnumerable DrawControlGroup(WorldRenderer wr, Actor self, int2 basePosition, PaletteReference palette)
- {
- var group = self.World.Selection.GetControlGroupForActor(self);
- if (group == null)
- yield break;
-
- pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
-
- var pos = basePosition - (0.5f * pipImages.Image.Size.XY).ToInt2() + new int2(9, 5);
- yield return new UISpriteRenderable(pipImages.Image, self.CenterPosition, pos, 0, palette, 1f);
- }
-
IEnumerable DrawPips(WorldRenderer wr, Actor self, int2 basePosition, PaletteReference palette)
{
var pipSources = self.TraitsImplementing();
diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs
new file mode 100644
index 0000000000..161d1d9c58
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs
@@ -0,0 +1,81 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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.Drawing;
+using System.Linq;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits.Render
+{
+ [Desc("Renders Ctrl groups using pixel art.")]
+ public class WithSpriteControlGroupDecorationInfo : ITraitInfo
+ {
+ [PaletteReference] public readonly string Palette = "chrome";
+
+ public readonly string Image = "pips";
+
+ [Desc("Sprite sequence used to render the control group 0-9 numbers.")]
+ [SequenceReference("Image")] public readonly string GroupSequence = "groups";
+
+ [Desc("Manual offset in screen pixel.")]
+ public readonly int2 ScreenOffset = new int2(9, 5);
+
+ public object Create(ActorInitializer init) { return new WithSpriteControlGroupDecoration(init.Self, this); }
+ }
+
+ public class WithSpriteControlGroupDecoration : IPostRenderSelection
+ {
+ public readonly WithSpriteControlGroupDecorationInfo Info;
+
+ readonly Actor self;
+ readonly Animation pipImages;
+
+ public WithSpriteControlGroupDecoration(Actor self, WithSpriteControlGroupDecorationInfo info)
+ {
+ this.self = self;
+ Info = info;
+
+ pipImages = new Animation(self.World, Info.Image);
+ }
+
+ IEnumerable IPostRenderSelection.RenderAfterWorld(WorldRenderer wr)
+ {
+ if (self.World.FogObscures(self))
+ yield break;
+
+ if (self.Owner != wr.World.LocalPlayer)
+ yield break;
+
+ var b = self.VisualBounds;
+ var pos = wr.ScreenPxPosition(self.CenterPosition);
+ var tl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Top));
+ var pal = wr.Palette(Info.Palette);
+
+ foreach (var r in DrawControlGroup(wr, self, tl, pal))
+ yield return r;
+ }
+
+ IEnumerable DrawControlGroup(WorldRenderer wr, Actor self, int2 basePosition, PaletteReference palette)
+ {
+ var group = self.World.Selection.GetControlGroupForActor(self);
+ if (group == null)
+ yield break;
+
+ pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
+
+ var pos = basePosition - (0.5f * pipImages.Image.Size.XY).ToInt2() + Info.ScreenOffset;
+ yield return new UISpriteRenderable(pipImages.Image, self.CenterPosition, pos, 0, palette, 1f);
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs
new file mode 100644
index 0000000000..bc2c6a99ca
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs
@@ -0,0 +1,126 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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.Drawing;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits.Render
+{
+ [Desc("Renders Ctrl groups using typeface.")]
+ public class WithTextControlGroupDecorationInfo : ITraitInfo, IRulesetLoaded
+ {
+ public readonly string Font = "TinyBold";
+
+ [Desc("Display in this color when not using the player color.")]
+ public readonly Color Color = Color.White;
+
+ [Desc("Use the player color of the current owner.")]
+ public readonly bool UsePlayerColor = false;
+
+ [Desc("The Z offset to apply when rendering this decoration.")]
+ public readonly int ZOffset = 1;
+
+ [Desc("Point in the actor's selection box used as reference for offsetting the decoration image. " +
+ "Possible values are combinations of Center, Top, Bottom, Left, Right.")]
+ public readonly ReferencePoints ReferencePoint = ReferencePoints.Bottom | ReferencePoints.Left;
+
+ [Desc("Manual offset in screen pixel.")]
+ public readonly int2 ScreenOffset = new int2(2, -2);
+
+ void IRulesetLoaded.RulesetLoaded(Ruleset rules, ActorInfo info)
+ {
+ if (!Game.ModData.Manifest.Fonts.ContainsKey(Font))
+ throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(Font));
+ }
+
+ public object Create(ActorInitializer init) { return new WithTextControlGroupDecoration(init.Self, this); }
+ }
+
+ public class WithTextControlGroupDecoration : IPostRenderSelection, INotifyCapture
+ {
+ readonly WithTextControlGroupDecorationInfo info;
+ readonly SpriteFont font;
+ readonly Actor self;
+
+ Color color;
+
+ public WithTextControlGroupDecoration(Actor self, WithTextControlGroupDecorationInfo info)
+ {
+ this.self = self;
+ this.info = info;
+
+ if (!Game.Renderer.Fonts.TryGetValue(info.Font, out font))
+ throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(info.Font));
+
+ color = info.UsePlayerColor ? self.Owner.Color.RGB : info.Color;
+ }
+
+ IEnumerable IPostRenderSelection.RenderAfterWorld(WorldRenderer wr)
+ {
+ if (self.World.FogObscures(self))
+ yield break;
+
+ if (self.Owner != wr.World.LocalPlayer)
+ yield break;
+
+ foreach (var r in DrawControlGroup(wr, self))
+ yield return r;
+ }
+
+ IEnumerable DrawControlGroup(WorldRenderer wr, Actor self)
+ {
+ var group = self.World.Selection.GetControlGroupForActor(self);
+ if (group == null)
+ yield break;
+
+ var bounds = self.VisualBounds;
+ var number = group.Value.ToString();
+ var halfSize = font.Measure(number) / 2;
+
+ var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
+ var sizeOffset = new int2();
+ if (info.ReferencePoint.HasFlag(ReferencePoints.Top))
+ {
+ boundsOffset -= new int2(0, bounds.Height / 2);
+ sizeOffset += new int2(0, halfSize.Y);
+ }
+ else if (info.ReferencePoint.HasFlag(ReferencePoints.Bottom))
+ {
+ boundsOffset += new int2(0, bounds.Height / 2);
+ sizeOffset -= new int2(0, halfSize.Y);
+ }
+
+ if (info.ReferencePoint.HasFlag(ReferencePoints.Left))
+ {
+ boundsOffset -= new int2(bounds.Width / 2, 0);
+ sizeOffset += new int2(halfSize.X, 0);
+ }
+ else if (info.ReferencePoint.HasFlag(ReferencePoints.Right))
+ {
+ boundsOffset += new int2(bounds.Width / 2, 0);
+ sizeOffset -= new int2(halfSize.X, 0);
+ }
+
+ var screenPos = wr.ScreenPxPosition(self.CenterPosition) + boundsOffset + sizeOffset + info.ScreenOffset;
+
+ yield return new TextRenderable(font, wr.ProjectedPosition(screenPos), info.ZOffset, color, number);
+ }
+
+ void INotifyCapture.OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
+ {
+ if (info.UsePlayerColor)
+ color = newOwner.Color.RGB;
+ }
+ }
+}