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; + } + } +}