Merge pull request #8388 from reaperrr/selectable-refactor1
Selectable bounds/selection box refactor
This commit is contained in:
@@ -41,12 +41,14 @@ namespace OpenRA
|
||||
public int Generation;
|
||||
|
||||
Lazy<Rectangle> bounds;
|
||||
Lazy<Rectangle> visualBounds;
|
||||
Lazy<IFacing> facing;
|
||||
Lazy<Health> health;
|
||||
Lazy<IOccupySpace> occupySpace;
|
||||
Lazy<IEffectiveOwner> effectiveOwner;
|
||||
|
||||
public Rectangle Bounds { get { return bounds.Value; } }
|
||||
public Rectangle VisualBounds { get { return visualBounds.Value; } }
|
||||
public IOccupySpace OccupiesSpace { get { return occupySpace.Value; } }
|
||||
public IEffectiveOwner EffectiveOwner { get { return effectiveOwner.Value; } }
|
||||
|
||||
@@ -110,6 +112,21 @@ namespace OpenRA
|
||||
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
||||
});
|
||||
|
||||
visualBounds = Exts.Lazy(() =>
|
||||
{
|
||||
var sd = Info.Traits.GetOrDefault<ISelectionDecorationsInfo>();
|
||||
if (sd == null || sd.SelectionBoxBounds == null)
|
||||
return bounds.Value;
|
||||
|
||||
var size = new int2(sd.SelectionBoxBounds[0], sd.SelectionBoxBounds[1]);
|
||||
|
||||
var offset = -size / 2;
|
||||
if (sd.SelectionBoxBounds.Length > 2)
|
||||
offset += new int2(sd.SelectionBoxBounds[2], sd.SelectionBoxBounds[3]);
|
||||
|
||||
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
||||
});
|
||||
|
||||
renderModifiers = TraitsImplementing<IRenderModifier>().ToArray();
|
||||
renders = TraitsImplementing<IRender>().ToArray();
|
||||
disables = TraitsImplementing<IDisable>().ToArray();
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace OpenRA.Graphics
|
||||
var health = actor.TraitOrDefault<Health>();
|
||||
|
||||
var screenPos = wr.ScreenPxPosition(pos);
|
||||
var bounds = actor.Bounds;
|
||||
var bounds = actor.VisualBounds;
|
||||
bounds.Offset(screenPos.X, screenPos.Y);
|
||||
|
||||
var start = new float2(bounds.Left + 1, bounds.Top);
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct SelectionBoxRenderable : IRenderable, IFinalizedRenderable
|
||||
{
|
||||
readonly WPos pos;
|
||||
readonly float scale;
|
||||
readonly Rectangle bounds;
|
||||
readonly Color color;
|
||||
|
||||
public SelectionBoxRenderable(Actor actor, Color color)
|
||||
: this(actor.CenterPosition, actor.Bounds, 1f, color) { }
|
||||
|
||||
public SelectionBoxRenderable(WPos pos, Rectangle bounds, float scale, Color color)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.bounds = bounds;
|
||||
this.scale = scale;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos; } }
|
||||
|
||||
public PaletteReference Palette { get { return null; } }
|
||||
public int ZOffset { get { return 0; } }
|
||||
public bool IsDecoration { get { return true; } }
|
||||
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
|
||||
public IRenderable WithZOffset(int newOffset) { return this; }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SelectionBoxRenderable(pos + vec, bounds, scale, color); }
|
||||
public IRenderable AsDecoration() { return this; }
|
||||
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var screenPos = wr.ScreenPxPosition(pos);
|
||||
var tl = screenPos + scale * new float2(bounds.Left, bounds.Top);
|
||||
var br = screenPos + scale * new float2(bounds.Right, bounds.Bottom);
|
||||
var tr = new float2(br.X, tl.Y);
|
||||
var bl = new float2(tl.X, br.Y);
|
||||
var u = new float2(4f / wr.Viewport.Zoom, 0);
|
||||
var v = new float2(0, 4f / wr.Viewport.Zoom);
|
||||
|
||||
var wlr = Game.Renderer.WorldLineRenderer;
|
||||
wlr.DrawLine(tl + u, tl, color);
|
||||
wlr.DrawLine(tl, tl + v, color);
|
||||
wlr.DrawLine(tr, tr - u, color);
|
||||
wlr.DrawLine(tr, tr + v, color);
|
||||
|
||||
wlr.DrawLine(bl, bl + u, color);
|
||||
wlr.DrawLine(bl, bl - v, color);
|
||||
wlr.DrawLine(br, br - u, color);
|
||||
wlr.DrawLine(br, br - v, color);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr) { }
|
||||
public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
|
||||
}
|
||||
}
|
||||
@@ -190,12 +190,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void DrawRollover(Actor unit)
|
||||
{
|
||||
var selectable = unit.TraitOrDefault<Selectable>();
|
||||
if (selectable != null)
|
||||
{
|
||||
if (selectable.Info.Selectable)
|
||||
new SelectionBarsRenderable(unit).Render(this);
|
||||
}
|
||||
if (unit.HasTrait<Selectable>())
|
||||
new SelectionBarsRenderable(unit).Render(this);
|
||||
}
|
||||
|
||||
public void DrawRangeCircle(WPos pos, WRange range, Color c)
|
||||
|
||||
@@ -180,7 +180,6 @@
|
||||
<Compile Include="Traits\Player\PlayerResources.cs" />
|
||||
<Compile Include="Traits\RevealsShroud.cs" />
|
||||
<Compile Include="Traits\Selectable.cs" />
|
||||
<Compile Include="Traits\SelectionDecorations.cs" />
|
||||
<Compile Include="Traits\Target.cs" />
|
||||
<Compile Include="Traits\TraitsInterfaces.cs" />
|
||||
<Compile Include="Traits\Util.cs" />
|
||||
@@ -235,7 +234,6 @@
|
||||
<Compile Include="Sound\OpenAlSound.cs" />
|
||||
<Compile Include="Sound\NullSound.cs" />
|
||||
<Compile Include="Effects\SpriteEffect.cs" />
|
||||
<Compile Include="Graphics\SelectionBoxRenderable.cs" />
|
||||
<Compile Include="Graphics\SelectionBarsRenderable.cs" />
|
||||
<Compile Include="Graphics\TargetLineRenderable.cs" />
|
||||
<Compile Include="Graphics\UISpriteRenderable.cs" />
|
||||
|
||||
@@ -63,8 +63,7 @@ namespace OpenRA.Orders
|
||||
|
||||
if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any()))
|
||||
{
|
||||
var selectable = underCursor.TraitOrDefault<Selectable>();
|
||||
if (selectable != null && selectable.Info.Selectable)
|
||||
if (underCursor.HasTrait<Selectable>())
|
||||
useSelect = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,14 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
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 readonly bool Selectable = true;
|
||||
public readonly int Priority = 10;
|
||||
|
||||
[Desc("Bounds for the selectable area.")]
|
||||
public readonly int[] Bounds = null;
|
||||
|
||||
[Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). "
|
||||
@@ -28,46 +25,13 @@ namespace OpenRA.Traits
|
||||
public object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
|
||||
}
|
||||
|
||||
public class Selectable : IPostRenderSelection
|
||||
public class Selectable
|
||||
{
|
||||
public readonly string Class = null;
|
||||
|
||||
public SelectableInfo Info;
|
||||
readonly Actor self;
|
||||
|
||||
public Selectable(Actor self, SelectableInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
Info = info;
|
||||
Class = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
|
||||
}
|
||||
|
||||
IEnumerable<WPos> ActivityTargetPath()
|
||||
{
|
||||
if (!self.IsInWorld || self.IsDead)
|
||||
yield break;
|
||||
|
||||
var activity = self.GetCurrentActivity();
|
||||
if (activity != null)
|
||||
{
|
||||
var targets = activity.GetTargets(self);
|
||||
yield return self.CenterPosition;
|
||||
|
||||
foreach (var t in targets.Where(t => t.Type != TargetType.Invalid))
|
||||
yield return t.CenterPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr)
|
||||
{
|
||||
if (!Info.Selectable)
|
||||
yield break;
|
||||
|
||||
yield return new SelectionBoxRenderable(self, Color.White);
|
||||
yield return new SelectionBarsRenderable(self);
|
||||
|
||||
if (self.World.LocalPlayer != null && self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
|
||||
yield return new TargetLineRenderable(ActivityTargetPath(), Color.Green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class SelectionDecorationsInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Palette = "chrome";
|
||||
|
||||
public object Create(ActorInitializer init) { return new SelectionDecorations(init.Self, this); }
|
||||
}
|
||||
|
||||
public class SelectionDecorations : IPostRenderSelection
|
||||
{
|
||||
// depends on the order of pips in TraitsInterfaces.cs!
|
||||
static readonly string[] PipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray", "pip-blue", "pip-ammo", "pip-ammoempty" };
|
||||
static readonly string[] TagStrings = { "", "tag-fake", "tag-primary" };
|
||||
|
||||
public SelectionDecorationsInfo Info;
|
||||
readonly Actor self;
|
||||
|
||||
public SelectionDecorations(Actor self, SelectionDecorationsInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
Info = info;
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr)
|
||||
{
|
||||
if (!self.Owner.IsAlliedWith(self.World.RenderPlayer) || self.World.FogObscures(self))
|
||||
yield break;
|
||||
|
||||
var b = self.Bounds;
|
||||
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 tm = wr.Viewport.WorldToViewPx(pos + new int2((b.Left + b.Right) / 2, b.Top));
|
||||
|
||||
foreach (var r in DrawControlGroup(wr, self, tl))
|
||||
yield return r;
|
||||
|
||||
foreach (var r in DrawPips(wr, self, bl))
|
||||
yield return r;
|
||||
|
||||
foreach (var r in DrawTags(wr, self, tm))
|
||||
yield return r;
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> DrawControlGroup(WorldRenderer wr, Actor self, int2 basePosition)
|
||||
{
|
||||
var group = self.World.Selection.GetControlGroupForActor(self);
|
||||
if (group == null)
|
||||
yield break;
|
||||
|
||||
var pipImages = new Animation(self.World, "pips");
|
||||
var pal = wr.Palette(Info.Palette);
|
||||
pipImages.PlayFetchIndex("groups", () => (int)group);
|
||||
pipImages.Tick();
|
||||
|
||||
var pos = basePosition - (0.5f * pipImages.Image.Size).ToInt2() + new int2(9, 5);
|
||||
yield return new UISpriteRenderable(pipImages.Image, pos, 0, pal, 1f);
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> DrawPips(WorldRenderer wr, Actor self, int2 basePosition)
|
||||
{
|
||||
var pipSources = self.TraitsImplementing<IPips>();
|
||||
if (!pipSources.Any())
|
||||
yield break;
|
||||
|
||||
var pipImages = new Animation(self.World, "pips");
|
||||
pipImages.PlayRepeating(PipStrings[0]);
|
||||
|
||||
var pipSize = pipImages.Image.Size.ToInt2();
|
||||
var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2));
|
||||
var pipxyOffset = new int2(0, 0);
|
||||
var pal = wr.Palette(Info.Palette);
|
||||
var width = self.Bounds.Width;
|
||||
|
||||
foreach (var pips in pipSources)
|
||||
{
|
||||
var thisRow = pips.GetPips(self);
|
||||
if (thisRow == null)
|
||||
continue;
|
||||
|
||||
foreach (var pip in thisRow)
|
||||
{
|
||||
if (pipxyOffset.X + pipSize.X >= width)
|
||||
pipxyOffset = new int2(0, pipxyOffset.Y - pipSize.Y);
|
||||
|
||||
pipImages.PlayRepeating(PipStrings[(int)pip]);
|
||||
pipxyOffset += new int2(pipSize.X, 0);
|
||||
|
||||
yield return new UISpriteRenderable(pipImages.Image, pipxyBase + pipxyOffset, 0, pal, 1f);
|
||||
}
|
||||
|
||||
// Increment row
|
||||
pipxyOffset = new int2(0, pipxyOffset.Y - (pipSize.Y + 1));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> DrawTags(WorldRenderer wr, Actor self, int2 basePosition)
|
||||
{
|
||||
var tagImages = new Animation(self.World, "pips");
|
||||
var pal = wr.Palette(Info.Palette);
|
||||
var tagxyOffset = new int2(0, 6);
|
||||
|
||||
foreach (var tags in self.TraitsImplementing<ITags>())
|
||||
{
|
||||
foreach (var tag in tags.GetTags())
|
||||
{
|
||||
if (tag == TagType.None)
|
||||
continue;
|
||||
|
||||
tagImages.PlayRepeating(TagStrings[(int)tag]);
|
||||
var pos = basePosition + tagxyOffset - (0.5f * tagImages.Image.Size).ToInt2();
|
||||
yield return new UISpriteRenderable(tagImages.Image, pos, 0, pal, 1f);
|
||||
|
||||
// Increment row
|
||||
tagxyOffset = tagxyOffset.WithY(tagxyOffset.Y + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,11 @@ namespace OpenRA.Traits
|
||||
|
||||
public interface ISeedableResource { void Seed(Actor self); }
|
||||
|
||||
public interface ISelectionDecorationsInfo
|
||||
{
|
||||
int[] SelectionBoxBounds { get; }
|
||||
}
|
||||
|
||||
public interface IVoiced
|
||||
{
|
||||
string VoiceSet { get; }
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace OpenRA.Widgets
|
||||
{
|
||||
if (!hasBox && World.Selection.Actors.Any() && !multiClick)
|
||||
{
|
||||
if (!(World.ScreenMap.ActorsAt(xy).Where(x => x.HasTrait<Selectable>() && x.Trait<Selectable>().Info.Selectable &&
|
||||
if (!(World.ScreenMap.ActorsAt(xy).Where(x => x.HasTrait<Selectable>() &&
|
||||
(x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x))).Any() && !mi.Modifiers.HasModifier(Modifiers.Ctrl) &&
|
||||
!mi.Modifiers.HasModifier(Modifiers.Alt) && UnitOrderGenerator.InputOverridesSelection(World, xy, mi)))
|
||||
{
|
||||
@@ -292,7 +292,7 @@ namespace OpenRA.Widgets
|
||||
var s = a.TraitOrDefault<Selectable>();
|
||||
|
||||
// sc == null means that units, that meet all other criteria, get selected
|
||||
return s != null && s.Info.Selectable && (selectionClasses == null || selectionClasses.Contains(s.Class));
|
||||
return s != null && (selectionClasses == null || selectionClasses.Contains(s.Class));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -303,11 +303,7 @@ namespace OpenRA.Widgets
|
||||
a = b;
|
||||
|
||||
return world.ScreenMap.ActorsInBox(a, b)
|
||||
.Where(x =>
|
||||
{
|
||||
var s = x.TraitOrDefault<Selectable>();
|
||||
return s != null && s.Info.Selectable && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x));
|
||||
})
|
||||
.Where(x => x.HasTrait<Selectable>() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x)))
|
||||
.GroupBy(x => x.GetSelectionPriority())
|
||||
.OrderByDescending(g => g.Key)
|
||||
.Select(g => g.AsEnumerable())
|
||||
|
||||
Reference in New Issue
Block a user