Introduce IMouseBounds and split/rework mouse rectangles.
The render bounds for an actor now include the area covered by bibs, shadows, and any other widgets. In many cases this area is much larger than we really want to consider for tooltips and mouse selection. An optional Margin is added to Selectable to support cases like infantry, where we want the mouse area of the actor to be larger than the drawn selection box.
This commit is contained in:
@@ -37,7 +37,8 @@ namespace OpenRA.Mods.Common.Orders
|
||||
{
|
||||
if (mi.Button == MouseButton.Left)
|
||||
{
|
||||
var underCursor = world.ScreenMap.ActorsAt(mi)
|
||||
var underCursor = world.ScreenMap.ActorsAtMouse(mi)
|
||||
.Select(a => a.Actor)
|
||||
.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.TraitsImplementing<T>()
|
||||
.Any(Exts.IsTraitEnabled));
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ namespace OpenRA.Mods.Common.Orders
|
||||
|
||||
static IEnumerable<Actor> FriendlyGuardableUnits(World world, MouseInput mi)
|
||||
{
|
||||
return world.ScreenMap.ActorsAt(mi)
|
||||
return world.ScreenMap.ActorsAtMouse(mi)
|
||||
.Select(a => a.Actor)
|
||||
.Where(a => !a.IsDead &&
|
||||
a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) &&
|
||||
a.Info.HasTraitInfo<GuardableInfo>() &&
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace OpenRA.Mods.Common.Orders
|
||||
if (mi.Button != MouseButton.Left)
|
||||
yield break;
|
||||
|
||||
var underCursor = world.ScreenMap.ActorsAt(mi)
|
||||
var underCursor = world.ScreenMap.ActorsAtMouse(mi)
|
||||
.Select(a => a.Actor)
|
||||
.FirstOrDefault(a => a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && !world.FogObscures(a));
|
||||
|
||||
if (underCursor == null)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Orders;
|
||||
using OpenRA.Traits;
|
||||
@@ -178,7 +179,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return prefix + "-blocked";
|
||||
}
|
||||
|
||||
public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
|
||||
public override bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
// Custom order generators always override selection
|
||||
return true;
|
||||
|
||||
@@ -128,6 +128,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
IRenderable[] renderables = null;
|
||||
Rectangle[] bounds = null;
|
||||
Rectangle mouseBounds = Rectangle.Empty;
|
||||
for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++)
|
||||
{
|
||||
var frozen = frozenStates[playerIndex].FrozenActor;
|
||||
@@ -139,6 +140,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
isRendering = true;
|
||||
renderables = self.Render(wr).ToArray();
|
||||
bounds = self.ScreenBounds(wr).ToArray();
|
||||
mouseBounds = self.MouseBounds(wr);
|
||||
|
||||
isRendering = false;
|
||||
}
|
||||
@@ -146,6 +148,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
frozen.NeedRenderables = false;
|
||||
frozen.Renderables = renderables;
|
||||
frozen.ScreenBounds = bounds;
|
||||
frozen.MouseBounds = mouseBounds;
|
||||
self.World.ScreenMap.AddOrUpdate(self.World.Players[playerIndex], frozen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits.Render
|
||||
@@ -16,17 +20,28 @@ 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 object Create(ActorInitializer init) { return new AutoRenderSize(this); }
|
||||
public object Create(ActorInitializer init) { return new AutoRenderSize(init.Self); }
|
||||
}
|
||||
|
||||
public class AutoRenderSize : IAutoRenderSize
|
||||
public class AutoRenderSize : IAutoRenderSize, IMouseBounds
|
||||
{
|
||||
public AutoRenderSize(AutoRenderSizeInfo info) { }
|
||||
readonly RenderSprites rs;
|
||||
|
||||
public AutoRenderSize(Actor self)
|
||||
{
|
||||
rs = self.Trait<RenderSprites>();
|
||||
}
|
||||
|
||||
public int2 RenderSize(Actor self)
|
||||
{
|
||||
var rs = self.Trait<RenderSprites>();
|
||||
return rs.AutoRenderSize(self);
|
||||
}
|
||||
|
||||
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return self.TraitsImplementing<IAutoMouseBounds>()
|
||||
.Select(s => s.AutoMouseoverBounds(self, wr))
|
||||
.FirstOrDefault(r => !r.IsEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
@@ -22,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public object Create(ActorInitializer init) { return new CustomRenderSize(this); }
|
||||
}
|
||||
|
||||
public class CustomRenderSize : IAutoRenderSize
|
||||
public class CustomRenderSize : IAutoRenderSize, IMouseBounds
|
||||
{
|
||||
readonly CustomRenderSizeInfo info;
|
||||
public CustomRenderSize(CustomRenderSizeInfo info) { this.info = info; }
|
||||
@@ -31,5 +33,20 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
return new int2(info.CustomBounds[0], info.CustomBounds[1]);
|
||||
}
|
||||
|
||||
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (info.CustomBounds == null)
|
||||
return Rectangle.Empty;
|
||||
|
||||
var size = new int2(info.CustomBounds[0], info.CustomBounds[1]);
|
||||
|
||||
var offset = -size / 2;
|
||||
if (info.CustomBounds.Length > 2)
|
||||
offset += new int2(info.CustomBounds[2], info.CustomBounds[3]);
|
||||
|
||||
var xy = wr.ScreenPxPosition(self.CenterPosition);
|
||||
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
}
|
||||
}
|
||||
|
||||
public readonly RenderSpritesInfo Info;
|
||||
readonly string faction;
|
||||
readonly RenderSpritesInfo info;
|
||||
readonly List<AnimationWrapper> anims = new List<AnimationWrapper>();
|
||||
string cachedImage;
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
|
||||
public RenderSprites(ActorInitializer init, RenderSpritesInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
Info = info;
|
||||
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
if (cachedImage != null)
|
||||
return cachedImage;
|
||||
|
||||
return cachedImage = info.GetImage(self.Info, self.World.Map.Rules.Sequences, faction);
|
||||
return cachedImage = Info.GetImage(self.Info, self.World.Map.Rules.Sequences, faction);
|
||||
}
|
||||
|
||||
public void UpdatePalette()
|
||||
@@ -199,7 +199,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
a.CachePalette(wr, owner);
|
||||
}
|
||||
|
||||
foreach (var r in a.Animation.Render(self, wr, a.PaletteReference, info.Scale))
|
||||
foreach (var r in a.Animation.Render(self, wr, a.PaletteReference, Info.Scale))
|
||||
yield return r;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
foreach (var a in anims)
|
||||
if (a.IsVisible)
|
||||
yield return a.Animation.ScreenBounds(self, wr, info.Scale);
|
||||
yield return a.Animation.ScreenBounds(self, wr, Info.Scale);
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
@@ -231,8 +231,8 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
// Use defaults
|
||||
if (palette == null)
|
||||
{
|
||||
palette = info.Palette ?? info.PlayerPalette;
|
||||
isPlayerPalette = info.Palette == null;
|
||||
palette = Info.Palette ?? Info.PlayerPalette;
|
||||
isPlayerPalette = Info.Palette == null;
|
||||
}
|
||||
|
||||
anims.Add(new AnimationWrapper(anim, palette, isPlayerPalette));
|
||||
@@ -281,7 +281,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
return anims.Where(b => b.IsVisible
|
||||
&& b.Animation.Animation.CurrentSequence != null)
|
||||
.Select(a => (a.Animation.Animation.Image.Size.XY * info.Scale).ToInt2())
|
||||
.Select(a => (a.Animation.Animation.Image.Size.XY * Info.Scale).ToInt2())
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
|
||||
@@ -97,11 +97,12 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
}
|
||||
}
|
||||
|
||||
public readonly RenderVoxelsInfo Info;
|
||||
|
||||
readonly List<ModelAnimation> components = new List<ModelAnimation>();
|
||||
readonly Dictionary<ModelAnimation, AnimationWrapper> wrappers = new Dictionary<ModelAnimation, AnimationWrapper>();
|
||||
|
||||
readonly Actor self;
|
||||
readonly RenderVoxelsInfo info;
|
||||
readonly BodyOrientation body;
|
||||
readonly WRot camera;
|
||||
readonly WRot lightSource;
|
||||
@@ -109,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
public RenderVoxels(Actor self, RenderVoxelsInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
this.info = info;
|
||||
Info = info;
|
||||
body = self.Trait<BodyOrientation>();
|
||||
camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256));
|
||||
lightSource = new WRot(WAngle.Zero, new WAngle(256) - info.LightPitch, info.LightYaw);
|
||||
@@ -133,16 +134,16 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
if (initializePalettes)
|
||||
{
|
||||
var paletteName = info.Palette ?? info.PlayerPalette + self.Owner.InternalName;
|
||||
var paletteName = Info.Palette ?? Info.PlayerPalette + self.Owner.InternalName;
|
||||
colorPalette = wr.Palette(paletteName);
|
||||
normalsPalette = wr.Palette(info.NormalsPalette);
|
||||
shadowPalette = wr.Palette(info.ShadowPalette);
|
||||
normalsPalette = wr.Palette(Info.NormalsPalette);
|
||||
shadowPalette = wr.Palette(Info.ShadowPalette);
|
||||
initializePalettes = false;
|
||||
}
|
||||
|
||||
return new IRenderable[] { new ModelRenderable(
|
||||
components, self.CenterPosition, 0, camera, info.Scale,
|
||||
lightSource, info.LightAmbientColor, info.LightDiffuseColor,
|
||||
components, self.CenterPosition, 0, camera, Info.Scale,
|
||||
lightSource, Info.LightAmbientColor, Info.LightDiffuseColor,
|
||||
colorPalette, normalsPalette, shadowPalette) };
|
||||
}
|
||||
|
||||
@@ -151,10 +152,10 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
var pos = self.CenterPosition;
|
||||
foreach (var c in components)
|
||||
if (c.IsVisible)
|
||||
yield return c.ScreenBounds(pos, wr, info.Scale);
|
||||
yield return c.ScreenBounds(pos, wr, Info.Scale);
|
||||
}
|
||||
|
||||
public string Image { get { return info.Image ?? self.Info.Name; } }
|
||||
public string Image { get { return Info.Image ?? self.Info.Name; } }
|
||||
public void Add(ModelAnimation m)
|
||||
{
|
||||
components.Add(m);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Traits;
|
||||
@@ -43,9 +44,10 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
}
|
||||
}
|
||||
|
||||
public class WithSpriteBody : PausableConditionalTrait<WithSpriteBodyInfo>, INotifyDamageStateChanged, INotifyBuildComplete
|
||||
public class WithSpriteBody : PausableConditionalTrait<WithSpriteBodyInfo>, INotifyDamageStateChanged, INotifyBuildComplete, IAutoMouseBounds
|
||||
{
|
||||
public readonly Animation DefaultAnimation;
|
||||
readonly RenderSprites rs;
|
||||
|
||||
public WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info)
|
||||
: this(init, info, () => 0) { }
|
||||
@@ -53,7 +55,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
protected WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info, Func<int> baseFacing)
|
||||
: base(info)
|
||||
{
|
||||
var rs = init.Self.Trait<RenderSprites>();
|
||||
rs = init.Self.Trait<RenderSprites>();
|
||||
|
||||
Func<bool> paused = () => IsTraitPaused &&
|
||||
DefaultAnimation.CurrentSequence.Name == NormalizeSequence(init.Self, Info.Sequence);
|
||||
@@ -125,5 +127,10 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
DamageStateChanged(self);
|
||||
}
|
||||
|
||||
Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return DefaultAnimation != null ? DefaultAnimation.ScreenBounds(wr, self.CenterPosition, WVec.Zero, rs.Info.Scale) : Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
@@ -39,21 +40,24 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
}
|
||||
}
|
||||
|
||||
public class WithVoxelBody : ConditionalTrait<WithVoxelBodyInfo>, IAutoSelectionSize, IAutoRenderSize
|
||||
public class WithVoxelBody : ConditionalTrait<WithVoxelBodyInfo>, IAutoSelectionSize, IAutoRenderSize, IAutoMouseBounds
|
||||
{
|
||||
readonly int2 size;
|
||||
readonly ModelAnimation modelAnimation;
|
||||
readonly RenderVoxels rv;
|
||||
|
||||
public WithVoxelBody(Actor self, WithVoxelBodyInfo info)
|
||||
: base(info)
|
||||
{
|
||||
var body = self.Trait<BodyOrientation>();
|
||||
var rv = self.Trait<RenderVoxels>();
|
||||
rv = self.Trait<RenderVoxels>();
|
||||
|
||||
var model = self.World.ModelCache.GetModelSequence(rv.Image, info.Sequence);
|
||||
rv.Add(new ModelAnimation(model, () => WVec.Zero,
|
||||
modelAnimation = new ModelAnimation(model, () => WVec.Zero,
|
||||
() => new[] { body.QuantizeOrientation(self, self.Orientation) },
|
||||
() => IsTraitDisabled, () => 0, info.ShowShadow));
|
||||
() => IsTraitDisabled, () => 0, info.ShowShadow);
|
||||
|
||||
rv.Add(modelAnimation);
|
||||
// Selection size
|
||||
var rvi = self.Info.TraitInfo<RenderVoxelsInfo>();
|
||||
var s = (int)(rvi.Scale * model.Size.Aggregate(Math.Max));
|
||||
@@ -62,5 +66,10 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
|
||||
public int2 SelectionSize(Actor self) { return size; }
|
||||
public int2 RenderSize(Actor self) { return size; }
|
||||
|
||||
Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return modelAnimation.ScreenBounds(self.CenterPosition, wr, rv.Info.Scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +229,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
}
|
||||
|
||||
var worldPixel = worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos);
|
||||
var underCursor = world.ScreenMap.ActorsAt(worldPixel)
|
||||
.Where(a => a.Info.HasTraitInfo<ITooltipInfo>() && !world.FogObscures(a))
|
||||
var underCursor = world.ScreenMap.ActorsAtMouse(worldPixel)
|
||||
.Where(a => a.Actor.Info.HasTraitInfo<ITooltipInfo>() && !world.FogObscures(a.Actor))
|
||||
.WithHighestSelectionPriority(worldPixel);
|
||||
|
||||
if (underCursor != null)
|
||||
@@ -245,7 +245,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return;
|
||||
}
|
||||
|
||||
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, worldPixel)
|
||||
var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, worldPixel)
|
||||
.Where(a => a.TooltipInfo != null && a.IsValid)
|
||||
.WithHighestSelectionPriority(worldPixel);
|
||||
|
||||
|
||||
@@ -107,11 +107,11 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
if (!IsValidDragbox && World.Selection.Actors.Any() && !multiClick)
|
||||
{
|
||||
var selectableActor = World.ScreenMap.ActorsAt(mousePos).Any(x =>
|
||||
var selectableActor = World.ScreenMap.ActorsAtMouse(mousePos).Select(a => a.Actor).Any(x =>
|
||||
x.Info.HasTraitInfo<SelectableInfo>() && (x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x)));
|
||||
|
||||
var uog = (UnitOrderGenerator)World.OrderGenerator;
|
||||
if (!selectableActor || uog.InputOverridesSelection(World, mousePos, mi))
|
||||
if (!selectableActor || uog.InputOverridesSelection(worldRenderer, World, mousePos, mi))
|
||||
{
|
||||
// Order units instead of selecting
|
||||
ApplyOrders(World, mi);
|
||||
@@ -124,7 +124,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
if (multiClick)
|
||||
{
|
||||
var unit = World.ScreenMap.ActorsAt(mousePos)
|
||||
var unit = World.ScreenMap.ActorsAtMouse(mousePos)
|
||||
.WithHighestSelectionPriority(mousePos);
|
||||
|
||||
if (unit != null && unit.Owner == (World.RenderPlayer ?? World.LocalPlayer))
|
||||
@@ -294,7 +294,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
static IEnumerable<Actor> SelectActorsOnScreen(World world, WorldRenderer wr, IEnumerable<string> selectionClasses, Player player)
|
||||
{
|
||||
return SelectActorsByOwnerAndSelectionClass(world.ScreenMap.ActorsInBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight), player, selectionClasses);
|
||||
var actors = world.ScreenMap.ActorsInMouseBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight).Select(a => a.Actor);
|
||||
return SelectActorsByOwnerAndSelectionClass(actors, player, selectionClasses);
|
||||
}
|
||||
|
||||
static IEnumerable<Actor> SelectActorsInWorld(World world, IEnumerable<string> selectionClasses, Player player)
|
||||
@@ -322,7 +323,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
if ((a - b).Length <= Game.Settings.Game.SelectionDeadzone)
|
||||
a = b;
|
||||
|
||||
return world.ScreenMap.ActorsInBox(a, b)
|
||||
return world.ScreenMap.ActorsInMouseBox(a, b)
|
||||
.Select(x => x.Actor)
|
||||
.Where(x => x.Info.HasTraitInfo<SelectableInfo>() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x)))
|
||||
.SubsetWithHighestSelectionPriority();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user