Introduce IDecorationBounds to replace Actor.SelectionOverlayBounds.

This commit is contained in:
Paul Chote
2017-12-08 17:31:36 +00:00
committed by reaperrr
parent 6f5d035e79
commit ff5b4b15b3
15 changed files with 148 additions and 64 deletions

View File

@@ -18,12 +18,12 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Automatically calculates the screen map boundaries from the sprite size.")]
public class AutoRenderSizeInfo : ITraitInfo, Requires<RenderSpritesInfo>, IAutoRenderSizeInfo
public class AutoRenderSizeInfo : ITraitInfo, Requires<RenderSpritesInfo>, IAutoRenderSizeInfo, IDecorationBoundsInfo
{
public object Create(ActorInitializer init) { return new AutoRenderSize(init.Self); }
}
public class AutoRenderSize : IAutoRenderSize, IMouseBounds
public class AutoRenderSize : IAutoRenderSize, IMouseBounds, IDecorationBounds
{
readonly RenderSprites rs;
@@ -37,11 +37,21 @@ namespace OpenRA.Mods.Common.Traits.Render
return rs.AutoRenderSize(self);
}
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
Rectangle Bounds(Actor self, WorldRenderer wr)
{
return self.TraitsImplementing<IAutoMouseBounds>()
.Select(s => s.AutoMouseoverBounds(self, wr))
.FirstOrDefault(r => !r.IsEmpty);
}
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
{
return Bounds(self, wr);
}
Rectangle IDecorationBounds.DecorationBounds(Actor self, WorldRenderer wr)
{
return Bounds(self, wr);
}
}
}

View File

@@ -16,15 +16,19 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Special case trait for actors that need to define targetable area and screen map bounds manually.")]
public class CustomRenderSizeInfo : ITraitInfo, IAutoRenderSizeInfo
public class CustomRenderSizeInfo : ITraitInfo, IAutoRenderSizeInfo, IDecorationBoundsInfo
{
[FieldLoader.Require]
public readonly int[] CustomBounds = null;
[Desc("Defines a custom rectangle for Decorations.",
"If null, CustomBounds will be used instead")]
public readonly int[] DecorationBounds = null;
public object Create(ActorInitializer init) { return new CustomRenderSize(this); }
}
public class CustomRenderSize : IAutoRenderSize, IMouseBounds
public class CustomRenderSize : IAutoRenderSize, IMouseBounds, IDecorationBounds
{
readonly CustomRenderSizeInfo info;
public CustomRenderSize(CustomRenderSizeInfo info) { this.info = info; }
@@ -48,5 +52,21 @@ namespace OpenRA.Mods.Common.Traits
var xy = wr.ScreenPxPosition(self.CenterPosition);
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
}
Rectangle IDecorationBounds.DecorationBounds(Actor self, WorldRenderer wr)
{
var bounds = info.DecorationBounds ?? info.CustomBounds;
if (bounds == null)
return Rectangle.Empty;
var size = new int2(bounds[0], bounds[1]);
var offset = -size / 2;
if (bounds.Length > 2)
offset += new int2(bounds[2], bounds[3]);
var xy = wr.ScreenPxPosition(self.CenterPosition);
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
}
}
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Traits;
@@ -18,7 +19,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Displays the player name above the unit")]
class RenderNameTagInfo : ITraitInfo
class RenderNameTagInfo : ITraitInfo, Requires<IDecorationBoundsInfo>
{
public readonly int MaxLength = 10;
@@ -32,6 +33,7 @@ namespace OpenRA.Mods.Common.Traits.Render
readonly SpriteFont font;
readonly Color color;
readonly string name;
readonly IDecorationBounds[] decorationBounds;
public RenderNameTag(Actor self, RenderNameTagInfo info)
{
@@ -42,15 +44,15 @@ namespace OpenRA.Mods.Common.Traits.Render
name = self.Owner.PlayerName.Substring(0, info.MaxLength);
else
name = self.Owner.PlayerName;
decorationBounds = self.TraitsImplementing<IDecorationBounds>().ToArray();
}
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.SelectionOverlayBounds;
bounds.Offset(pos.X, pos.Y);
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty);
var spaceBuffer = (int)(10 / wr.Viewport.Zoom);
var effectPos = wr.ProjectedPosition(new int2(pos.X, bounds.Y - spaceBuffer));
var effectPos = wr.ProjectedPosition(new int2((bounds.Left + bounds.Right) / 2, bounds.Y - spaceBuffer));
return new IRenderable[] { new TextRenderable(font, effectPos, 0, color, name) };
}

View File

@@ -18,7 +18,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
public class SelectionDecorationsInfo : ITraitInfo, ISelectionDecorationsInfo
public class SelectionDecorationsInfo : ITraitInfo, ISelectionDecorationsInfo, Requires<IDecorationBoundsInfo>
{
[PaletteReference] public readonly string Palette = "chrome";
@@ -48,6 +48,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public readonly SelectionDecorationsInfo Info;
readonly IDecorationBounds[] decorationBounds;
readonly Animation pipImages;
IPips[] pipSources;
@@ -55,6 +56,7 @@ namespace OpenRA.Mods.Common.Traits.Render
{
Info = info;
decorationBounds = self.TraitsImplementing<IDecorationBounds>().ToArray();
pipImages = new Animation(self.World, Info.Image);
}
@@ -92,6 +94,7 @@ namespace OpenRA.Mods.Common.Traits.Render
var selected = self.World.Selection.Contains(self);
var regularWorld = self.World.Type == WorldType.Regular;
var statusBars = Game.Settings.Game.StatusBars;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty);
// Health bars are shown when:
// * actor is selected
@@ -107,10 +110,10 @@ namespace OpenRA.Mods.Common.Traits.Render
var displayExtra = selected || (regularWorld && statusBars != StatusBarsType.Standard);
if (Info.RenderSelectionBox && selected)
yield return new SelectionBoxRenderable(self, Info.SelectionBoxColor);
yield return new SelectionBoxRenderable(self, bounds, Info.SelectionBoxColor);
if (Info.RenderSelectionBars && (displayHealth || displayExtra))
yield return new SelectionBarsRenderable(self, displayHealth, displayExtra);
yield return new SelectionBarsRenderable(self, bounds, displayHealth, displayExtra);
// Target lines and pips are always only displayed for selected allied actors
if (!selected || !self.Owner.IsAlliedWith(wr.World.RenderPlayer))
@@ -119,31 +122,28 @@ namespace OpenRA.Mods.Common.Traits.Render
if (self.World.LocalPlayer != null && self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
yield return new TargetLineRenderable(ActivityTargetPath(self), Color.Green);
foreach (var r in DrawPips(self, wr))
foreach (var r in DrawPips(self, bounds, wr))
yield return r;
}
IEnumerable<IRenderable> DrawPips(Actor self, WorldRenderer wr)
IEnumerable<IRenderable> DrawPips(Actor self, Rectangle bounds, WorldRenderer wr)
{
if (pipSources.Length == 0)
return Enumerable.Empty<IRenderable>();
var b = self.SelectionOverlayBounds;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
var pal = wr.Palette(Info.Palette);
return DrawPips(self, bl, pal);
return DrawPipsInner(self, bounds, wr);
}
IEnumerable<IRenderable> DrawPips(Actor self, int2 basePosition, PaletteReference palette)
IEnumerable<IRenderable> DrawPipsInner(Actor self, Rectangle bounds, WorldRenderer wr)
{
pipImages.PlayRepeating(PipStrings[0]);
var palette = wr.Palette(Info.Palette);
var basePosition = wr.Viewport.WorldToViewPx(new int2(bounds.Left, bounds.Bottom));
var pipSize = pipImages.Image.Size.XY.ToInt2();
var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2));
var pipxyOffset = new int2(0, 0);
var width = self.SelectionOverlayBounds.Width;
var width = bounds.Width;
foreach (var pips in pipSources)
{

View File

@@ -27,8 +27,8 @@ namespace OpenRA.Mods.Common.Traits.Render
Right = 8,
}
[Desc("Displays a custom UI overlay relative to the selection box.")]
public class WithDecorationInfo : ConditionalTraitInfo
[Desc("Displays a custom UI overlay relative to the actor's mouseover bounds.")]
public class WithDecorationInfo : ConditionalTraitInfo, Requires<IDecorationBoundsInfo>
{
[Desc("Image used for this decoration. Defaults to the actor's type.")]
public readonly string Image = null;
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public class WithDecoration : ConditionalTrait<WithDecorationInfo>, ITick, IRenderAboveShroud, IRenderAboveShroudWhenSelected
{
protected readonly Animation Anim;
readonly IDecorationBounds[] decorationBounds;
readonly string image;
public WithDecoration(Actor self, WithDecorationInfo info)
@@ -67,6 +67,7 @@ namespace OpenRA.Mods.Common.Traits.Render
image = info.Image ?? self.Info.Name;
Anim = new Animation(self.World, image, () => self.World.Paused);
Anim.PlayRepeating(info.Sequence);
decorationBounds = self.TraitsImplementing<IDecorationBounds>().ToArray();
}
protected virtual bool ShouldRender(Actor self)
@@ -99,7 +100,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (!ShouldRender(self) || self.World.FogObscures(self))
return Enumerable.Empty<IRenderable>();
var bounds = self.SelectionOverlayBounds;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty);
var halfSize = (0.5f * Anim.Image.Size.XY).ToInt2();
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
@@ -126,7 +127,7 @@ namespace OpenRA.Mods.Common.Traits.Render
sizeOffset -= new int2(halfSize.X, 0);
}
var pxPos = wr.Viewport.WorldToViewPx(wr.ScreenPxPosition(self.CenterPosition) + boundsOffset) + sizeOffset;
var pxPos = wr.Viewport.WorldToViewPx(boundsOffset) + sizeOffset;
return new IRenderable[] { new UISpriteRenderable(Anim.Image, self.CenterPosition, pxPos, Info.ZOffset, wr.Palette(Info.Palette), 1f) };
}

View File

@@ -19,7 +19,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Renders Ctrl groups using pixel art.")]
public class WithSpriteControlGroupDecorationInfo : ITraitInfo
public class WithSpriteControlGroupDecorationInfo : ITraitInfo, Requires<IDecorationBoundsInfo>
{
[PaletteReference] public readonly string Palette = "chrome";
@@ -38,12 +38,14 @@ namespace OpenRA.Mods.Common.Traits.Render
public class WithSpriteControlGroupDecoration : IRenderAboveShroudWhenSelected
{
public readonly WithSpriteControlGroupDecorationInfo Info;
readonly IDecorationBounds[] decorationBounds;
readonly Animation pipImages;
public WithSpriteControlGroupDecoration(Actor self, WithSpriteControlGroupDecorationInfo info)
{
Info = info;
decorationBounds = self.TraitsImplementing<IDecorationBounds>().ToArray();
pipImages = new Animation(self.World, Info.Image);
}
@@ -68,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits.Render
pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
var bounds = self.SelectionOverlayBounds;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty);
var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom);
if (Info.ReferencePoint.HasFlag(ReferencePoints.Top))
boundsOffset -= new float2(0, 0.5f * bounds.Height);
@@ -82,7 +84,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (Info.ReferencePoint.HasFlag(ReferencePoints.Right))
boundsOffset += new float2(0.5f * bounds.Width, 0);
var pxPos = wr.Viewport.WorldToViewPx(wr.ScreenPxPosition(self.CenterPosition) + boundsOffset.ToInt2()) - (0.5f * pipImages.Image.Size.XY).ToInt2();
var pxPos = wr.Viewport.WorldToViewPx(boundsOffset.ToInt2()) - (0.5f * pipImages.Image.Size.XY).ToInt2();
yield return new UISpriteRenderable(pipImages.Image, self.CenterPosition, pxPos, 0, palette, 1f);
}
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Traits;
@@ -18,7 +19,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Renders Ctrl groups using typeface.")]
public class WithTextControlGroupDecorationInfo : ITraitInfo, IRulesetLoaded
public class WithTextControlGroupDecorationInfo : ITraitInfo, IRulesetLoaded, Requires<IDecorationBoundsInfo>
{
public readonly string Font = "TinyBold";
@@ -50,6 +51,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public class WithTextControlGroupDecoration : IRenderAboveShroudWhenSelected, INotifyOwnerChanged
{
readonly WithTextControlGroupDecorationInfo info;
readonly IDecorationBounds[] decorationBounds;
readonly SpriteFont font;
Color color;
@@ -61,6 +63,7 @@ namespace OpenRA.Mods.Common.Traits.Render
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));
decorationBounds = self.TraitsImplementing<IDecorationBounds>().ToArray();
color = info.UsePlayerColor ? self.Owner.Color.RGB : info.Color;
}
@@ -82,7 +85,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (group == null)
yield break;
var bounds = self.SelectionOverlayBounds;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty);
var number = group.Value.ToString();
var halfSize = font.Measure(number) / 2;
@@ -110,7 +113,7 @@ namespace OpenRA.Mods.Common.Traits.Render
sizeOffset -= new int2(halfSize.X, 0);
}
var screenPos = wr.ScreenPxPosition(self.CenterPosition) + boundsOffset + sizeOffset + info.ScreenOffset;
var screenPos = boundsOffset + sizeOffset + info.ScreenOffset;
yield return new TextRenderable(font, wr.ProjectedPosition(screenPos), info.ZOffset, color, number);
}

View File

@@ -20,7 +20,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Displays a text overlay relative to the selection box.")]
public class WithTextDecorationInfo : ConditionalTraitInfo
public class WithTextDecorationInfo : ConditionalTraitInfo, Requires<IDecorationBoundsInfo>
{
[FieldLoader.Require] [Translate] public readonly string Text = null;
@@ -59,12 +59,14 @@ namespace OpenRA.Mods.Common.Traits.Render
public class WithTextDecoration : ConditionalTrait<WithTextDecorationInfo>, IRender, IRenderAboveShroudWhenSelected, INotifyOwnerChanged
{
readonly SpriteFont font;
readonly IDecorationBounds[] decorationBounds;
Color color;
public WithTextDecoration(Actor self, WithTextDecorationInfo info)
: base(info)
{
font = Game.Renderer.Fonts[info.Font];
decorationBounds = self.TraitsImplementing<IDecorationBounds>().ToArray();
color = Info.UsePlayerColor ? self.Owner.Color.RGB : Info.Color;
}
@@ -101,7 +103,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (!ShouldRender(self) || self.World.FogObscures(self))
return Enumerable.Empty<IRenderable>();
var bounds = self.SelectionOverlayBounds;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty);
var halfSize = font.Measure(Info.Text) / 2;
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
@@ -128,8 +130,7 @@ namespace OpenRA.Mods.Common.Traits.Render
sizeOffset -= new int2(halfSize.X, 0);
}
var screenPos = wr.ScreenPxPosition(self.CenterPosition) + boundsOffset + sizeOffset;
return new IRenderable[] { new TextRenderable(font, wr.ProjectedPosition(screenPos), Info.ZOffset, color, Info.Text) };
return new IRenderable[] { new TextRenderable(font, wr.ProjectedPosition(boundsOffset + sizeOffset), Info.ZOffset, color, Info.Text) };
}
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)