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

@@ -16,7 +16,7 @@ using OpenRA.Graphics;
namespace OpenRA.Traits
{
[Desc("This actor is selectable. Defines bounds of selectable area, selection class and selection priority.")]
public class SelectableInfo : ITraitInfo
public class SelectableInfo : ITraitInfo, IDecorationBoundsInfo
{
public readonly int Priority = 10;
@@ -35,7 +35,7 @@ namespace OpenRA.Traits
public object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
}
public class Selectable : IMouseBounds
public class Selectable : IMouseBounds, IDecorationBounds
{
public readonly string Class = null;
@@ -61,5 +61,20 @@ namespace OpenRA.Traits
var xy = wr.ScreenPxPosition(self.CenterPosition) + offset;
return new Rectangle(xy.X, xy.Y, size.X + 2 * Info.Margin, size.Y + 2 * Info.Margin);
}
Rectangle IDecorationBounds.DecorationBounds(Actor self, WorldRenderer wr)
{
if (Info.Bounds == null)
return Rectangle.Empty;
var size = new int2(Info.Bounds[0], Info.Bounds[1]);
var offset = -size / 2;
if (Info.Bounds.Length > 2)
offset += new int2(Info.Bounds[2], Info.Bounds[3]);
var xy = wr.ScreenPxPosition(self.CenterPosition) + offset;
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
}
}
}

View File

@@ -109,6 +109,10 @@ namespace OpenRA.Traits
public interface IMouseBounds { Rectangle MouseoverBounds(Actor self, WorldRenderer wr); }
public interface IAutoMouseBounds { Rectangle AutoMouseoverBounds(Actor self, WorldRenderer wr); }
// HACK: This provides a shim for legacy code until it can be rewritten
public interface IDecorationBounds { Rectangle DecorationBounds(Actor self, WorldRenderer wr); }
public interface IDecorationBoundsInfo : ITraitInfoInterface { }
public interface IAutoRenderSizeInfo : ITraitInfoInterface { }
public interface IAutoRenderSize { int2 RenderSize(Actor self); }

View File

@@ -160,8 +160,16 @@ namespace OpenRA.Mods.Cnc.Traits
var targetUnits = power.UnitsInRange(xy).Where(a => !world.FogObscures(a));
foreach (var unit in targetUnits)
{
if (unit.CanBeViewedByPlayer(manager.Self.Owner))
yield return new SelectionBoxRenderable(unit, Color.Red);
{
var bounds = unit.TraitsImplementing<IDecorationBounds>()
.Select(b => b.DecorationBounds(unit, wr))
.FirstOrDefault(b => !b.IsEmpty);
yield return new SelectionBoxRenderable(unit, bounds, Color.Red);
}
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world)
@@ -270,8 +278,16 @@ namespace OpenRA.Mods.Cnc.Traits
}
foreach (var unit in power.UnitsInRange(sourceLocation))
{
if (unit.CanBeViewedByPlayer(manager.Self.Owner))
yield return new SelectionBoxRenderable(unit, Color.Red);
{
var bounds = unit.TraitsImplementing<IDecorationBounds>()
.Select(b => b.DecorationBounds(unit, wr))
.FirstOrDefault(b => !b.IsEmpty);
yield return new SelectionBoxRenderable(unit, bounds, Color.Red);
}
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world)

View File

@@ -21,19 +21,21 @@ namespace OpenRA.Mods.Common.Graphics
readonly Actor actor;
readonly bool displayHealth;
readonly bool displayExtra;
readonly Rectangle decorationBounds;
public SelectionBarsRenderable(Actor actor, bool displayHealth, bool displayExtra)
: this(actor.CenterPosition, actor)
public SelectionBarsRenderable(Actor actor, Rectangle decorationBounds, bool displayHealth, bool displayExtra)
: this(actor.CenterPosition, actor, decorationBounds)
{
this.displayHealth = displayHealth;
this.displayExtra = displayExtra;
}
public SelectionBarsRenderable(WPos pos, Actor actor)
public SelectionBarsRenderable(WPos pos, Actor actor, Rectangle decorationBounds)
: this()
{
this.pos = pos;
this.actor = actor;
this.decorationBounds = decorationBounds;
}
public WPos Pos { get { return pos; } }
@@ -46,7 +48,7 @@ namespace OpenRA.Mods.Common.Graphics
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return new SelectionBarsRenderable(pos + vec, actor); }
public IRenderable OffsetBy(WVec vec) { return new SelectionBarsRenderable(pos + vec, actor, decorationBounds); }
public IRenderable AsDecoration() { return this; }
void DrawExtraBars(WorldRenderer wr, float3 start, float3 end)
@@ -150,11 +152,8 @@ namespace OpenRA.Mods.Common.Graphics
var health = actor.TraitOrDefault<IHealth>();
var screenPos = wr.Screen3DPxPosition(pos);
var bounds = actor.SelectionOverlayBounds;
bounds.Offset((int)screenPos.X, (int)screenPos.Y);
var start = new float3(bounds.Left + 1, bounds.Top, screenPos.Z);
var end = new float3(bounds.Right - 1, bounds.Top, screenPos.Z);
var start = new float3(decorationBounds.Left + 1, decorationBounds.Top, screenPos.Z);
var end = new float3(decorationBounds.Right - 1, decorationBounds.Top, screenPos.Z);
if (DisplayHealth)
DrawHealthBar(wr, health, start, end);

View File

@@ -17,16 +17,16 @@ namespace OpenRA.Mods.Common.Graphics
public struct SelectionBoxRenderable : IRenderable, IFinalizedRenderable
{
readonly WPos pos;
readonly Rectangle visualBounds;
readonly Rectangle decorationBounds;
readonly Color color;
public SelectionBoxRenderable(Actor actor, Color color)
: this(actor.CenterPosition, actor.SelectionOverlayBounds, color) { }
public SelectionBoxRenderable(Actor actor, Rectangle decorationBounds, Color color)
: this(actor.CenterPosition, decorationBounds, color) { }
public SelectionBoxRenderable(WPos pos, Rectangle visualBounds, Color color)
public SelectionBoxRenderable(WPos pos, Rectangle decorationBounds, Color color)
{
this.pos = pos;
this.visualBounds = visualBounds;
this.decorationBounds = decorationBounds;
this.color = color;
}
@@ -38,18 +38,18 @@ namespace OpenRA.Mods.Common.Graphics
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return new SelectionBoxRenderable(pos + vec, visualBounds, color); }
public IRenderable OffsetBy(WVec vec) { return new SelectionBoxRenderable(pos + vec, decorationBounds, color); }
public IRenderable AsDecoration() { return this; }
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
var iz = 1 / wr.Viewport.Zoom;
var screenPos = wr.Screen3DPxPosition(pos);
var tl = screenPos + new float2(visualBounds.Left, visualBounds.Top);
var br = screenPos + new float2(visualBounds.Right, visualBounds.Bottom);
var tr = new float3(br.X, tl.Y, screenPos.Z);
var bl = new float3(tl.X, br.Y, screenPos.Z);
var screenDepth = wr.Screen3DPxPosition(pos).Z;
var tl = new float2(decorationBounds.Left, decorationBounds.Top);
var br = new float2(decorationBounds.Right, decorationBounds.Bottom);
var tr = new float3(br.X, tl.Y, screenDepth);
var bl = new float3(tl.X, br.Y, screenDepth);
var u = new float2(4 * iz, 0);
var v = new float2(0, 4 * iz);

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)

View File

@@ -134,7 +134,12 @@ namespace OpenRA.Mods.Common.Traits
{
var xy = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
foreach (var unit in power.UnitsInRange(xy))
yield return new SelectionBoxRenderable(unit, Color.Red);
{
var bounds = unit.TraitsImplementing<IDecorationBounds>()
.Select(b => b.DecorationBounds(unit, wr))
.FirstOrDefault(b => !b.IsEmpty);
yield return new SelectionBoxRenderable(unit, bounds, Color.Red);
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world)

View File

@@ -50,7 +50,13 @@ namespace OpenRA.Mods.Common.Widgets
{
// TODO: Integrate this with SelectionDecorations to unhardcode the *Renderable
if (unit.Info.HasTraitInfo<SelectableInfo>())
new SelectionBarsRenderable(unit, true, true).Render(worldRenderer);
{
var bounds = unit.TraitsImplementing<IDecorationBounds>()
.Select(b => b.DecorationBounds(unit, worldRenderer))
.FirstOrDefault(b => !b.IsEmpty);
new SelectionBarsRenderable(unit, bounds, true, true).Render(worldRenderer);
}
}
public override void Draw()