diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 9822fde854..242b6af3e0 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..5610ad523b --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs @@ -0,0 +1,105 @@ +#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("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.Top | ReferencePoints.Left; + + 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 pal = wr.Palette(Info.Palette); + foreach (var r in DrawControlGroup(wr, self, pal)) + yield return r; + } + + IEnumerable DrawControlGroup(WorldRenderer wr, Actor self, PaletteReference palette) + { + var group = self.World.Selection.GetControlGroupForActor(self); + if (group == null) + yield break; + + pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group); + + var bounds = self.VisualBounds; + var halfSize = (0.5f * pipImages.Image.Size.XY).ToInt2(); + + var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2; + var sizeOffset = -halfSize; + 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 pxPos = wr.Viewport.WorldToViewPx(wr.ScreenPxPosition(self.CenterPosition) + boundsOffset) + sizeOffset; + yield return new UISpriteRenderable(pipImages.Image, self.CenterPosition, pxPos, 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; + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index f764b176da..ecc4c7973f 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -270,6 +270,16 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20160717) + { + if (depth == 0) + { + var selectionDecorations = node.Value.Nodes.FirstOrDefault(n => n.Key == "SelectionDecorations"); + if (selectionDecorations != null) + node.Value.Nodes.Add(selectionDecorations = new MiniYamlNode("WithSpriteControlGroup", "")); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 180320f53b..aafad4d330 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -69,6 +69,7 @@ Beach: 50 TurnSpeed: 5 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24,24 Targetable: @@ -133,6 +134,7 @@ UpgradeTypes: airborne UpgradeMinEnabledLevel: 1 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24,24 Aircraft: @@ -191,6 +193,8 @@ PathingCost: 300 Beach: 80 SelectionDecorations: + WithSpriteControlGroupDecoration: + ReferencePoint: Top Selectable: Bounds: 12,17,0,-6 Targetable: @@ -351,6 +355,7 @@ Beach: 80 Voice: Move SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24,24 Targetable: @@ -398,6 +403,7 @@ BlueTiberium: 100 Beach: 60 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24,24 Targetable: @@ -463,6 +469,7 @@ TerrainSpeeds: Water: 100 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Targetable: TargetTypes: Ground, Water @@ -486,6 +493,7 @@ Inherits@2: ^SpriteActor Huntable: SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Priority: 3 Targetable: diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 255f4e5f86..4396be601b 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -74,6 +74,7 @@ Dune: 50 TurnSpeed: 5 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 32,32 Targetable: @@ -183,6 +184,7 @@ Dune: 80 Rough: 80 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 12,20,0,-4 Targetable: @@ -245,6 +247,7 @@ Inherits@2: ^SpriteActor Huntable: SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Priority: 2 RevealsShroud: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index a50aed2194..105f5cf9fb 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -82,6 +82,7 @@ Beach: 40 TurnSpeed: 5 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24, 24 Targetable: @@ -189,6 +190,8 @@ Gems: 80 Beach: 80 SelectionDecorations: + WithSpriteControlGroupDecoration: + ReferencePoint: Top Selectable: Bounds: 12,18,0,-8 Targetable: @@ -316,6 +319,7 @@ TerrainSpeeds: Water: 100 SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24,24 Targetable: @@ -357,6 +361,7 @@ AppearsOnRadar: UseLocation: true SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Bounds: 24,24 Aircraft: @@ -420,6 +425,7 @@ Inherits@2: ^IronCurtainable Inherits@3: ^SpriteActor SelectionDecorations: + WithSpriteControlGroupDecoration: Selectable: Priority: 3 Targetable: diff --git a/mods/ts/bits/pipsra.shp b/mods/ts/bits/pipsra.shp deleted file mode 100644 index da81c9bf59..0000000000 Binary files a/mods/ts/bits/pipsra.shp and /dev/null differ diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 36e1ad260d..7cb6c4bc73 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -93,6 +93,7 @@ Inherits@2: ^SpriteActor Inherits@3: ^Cloakable Huntable: + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Selectable: @@ -268,6 +269,7 @@ BlueTiberium: 90 PathingCost: 90 Veins: 50 + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Selectable: @@ -439,6 +441,7 @@ Voice: Move Selectable: Bounds: 40,24 + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Voiced: @@ -540,6 +543,7 @@ UpgradeTypes: airborne UpgradeMinEnabledLevel: 1 Selectable: + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Aircraft: @@ -633,6 +637,7 @@ Tiberium: 100 BlueTiberium: 100 Veins: 100 + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Selectable: @@ -747,6 +752,7 @@ TerrainTypes: Clear, Road, DirtRoad, Rough WithSpriteBody: WithMakeAnimation: + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Selectable: @@ -784,6 +790,7 @@ Range: 5c0 Selectable: Bounds: 40,24 + WithTextControlGroupDecoration: SelectionDecorations: Palette: pips Voiced: diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml index bbc9bd0c8e..d98cd6e6af 100644 --- a/mods/ts/sequences/misc.yaml +++ b/mods/ts/sequences/misc.yaml @@ -87,9 +87,6 @@ darken: pips: medic: Start: 6 - groups: pipsra #TODO: backfall to RA asset - Start: 8 - Length: 10 pip-empty: pips2 pip-green: pips2 Start: 1