Move Interactable and Selectable to Mods.Common.

This commit is contained in:
Paul Chote
2020-02-29 11:29:06 +00:00
committed by atlimit8
parent c5139fb6c2
commit 73a78eadb1
9 changed files with 50 additions and 27 deletions

View File

@@ -11,6 +11,7 @@
using System;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint

View File

@@ -0,0 +1,80 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Used to enable mouse interaction on actors that are not Selectable.")]
public class InteractableInfo : ITraitInfo, IMouseBoundsInfo, IDecorationBoundsInfo
{
[Desc("Defines a custom rectangle for mouse interaction with the actor.",
"If null, the engine will guess an appropriate size based on the With*Body trait.",
"The first two numbers define the width and height of the rectangle.",
"The (optional) second two numbers define an x and y offset from the actor center.")]
public readonly int[] Bounds = null;
[Desc("Defines a custom rectangle for Decorations (e.g. the selection box).",
"If null, Bounds will be used instead")]
public readonly int[] DecorationBounds = null;
public virtual object Create(ActorInitializer init) { return new Interactable(this); }
}
public class Interactable : INotifyCreated, IMouseBounds, IDecorationBounds
{
readonly InteractableInfo info;
IAutoMouseBounds[] autoBounds;
public Interactable(InteractableInfo info)
{
this.info = info;
}
void INotifyCreated.Created(Actor self)
{
autoBounds = self.TraitsImplementing<IAutoMouseBounds>().ToArray();
}
Rectangle AutoBounds(Actor self, WorldRenderer wr)
{
return autoBounds.Select(s => s.AutoMouseoverBounds(self, wr)).FirstOrDefault(r => !r.IsEmpty);
}
Rectangle Bounds(Actor self, WorldRenderer wr, int[] bounds)
{
if (bounds == null)
return AutoBounds(self, wr);
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) + offset;
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
}
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
{
return Bounds(self, wr, info.Bounds);
}
Rectangle IDecorationBounds.DecorationBounds(Actor self, WorldRenderer wr)
{
return Bounds(self, wr, info.DecorationBounds ?? info.Bounds);
}
}
}

View File

@@ -0,0 +1,56 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("This actor is selectable. Defines bounds of selectable area, selection class, selection priority and selection priority modifiers.")]
public class SelectableInfo : InteractableInfo, ISelectableInfo
{
public readonly int Priority = 10;
[Desc("Allow selection priority to be modified using a hotkey.",
"Valid values are None (priority is not affected by modifiers)",
"Ctrl (priority is raised when Ctrl pressed) and",
"Alt (priority is raised when Alt pressed).")]
public readonly SelectionPriorityModifiers PriorityModifiers = SelectionPriorityModifiers.None;
[Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). "
+ "Defaults to the actor name when not defined or inherited.")]
public readonly string Class = null;
[VoiceReference]
public readonly string Voice = "Select";
public override object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
int ISelectableInfo.Priority { get { return Priority; } }
SelectionPriorityModifiers ISelectableInfo.PriorityModifiers { get { return PriorityModifiers; } }
string ISelectableInfo.Voice { get { return Voice; } }
}
public class Selectable : Interactable, ISelectable
{
readonly string selectionClass = null;
public readonly SelectableInfo Info;
public Selectable(Actor self, SelectableInfo info)
: base(info)
{
selectionClass = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
Info = info;
}
string ISelectable.Class { get { return selectionClass; } }
}
}

View File

@@ -123,13 +123,12 @@ namespace OpenRA.Mods.Common.Traits
// Play the selection voice from one of the selected actors
// TODO: This probably should only be considering the newly selected actors
// TODO: Ship this into an INotifySelection trait to remove the engine dependency on Selectable
foreach (var actor in actors)
{
if (actor.Owner != world.LocalPlayer || !actor.IsInWorld)
continue;
var selectable = actor.Info.TraitInfoOrDefault<SelectableInfo>();
var selectable = actor.Info.TraitInfoOrDefault<ISelectableInfo>();
if (selectable == null || !actor.HasVoice(selectable.Voice))
continue;

View File

@@ -636,4 +636,10 @@ namespace OpenRA.Mods.Common.Traits
{
void NotifyTimerExpired(Actor self);
}
[RequireExplicitImplementation]
public interface ISelectable
{
string Class { get; }
}
}

View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Effects;
using OpenRA.Mods.Common.Traits;
using OpenRA.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -127,7 +128,7 @@ namespace OpenRA.Mods.Common.Widgets
if (!IsValidDragbox && World.Selection.Actors.Any() && !multiClick)
{
var selectableActor = World.ScreenMap.ActorsAtMouse(mousePos).Select(a => a.Actor).Any(x =>
x.Info.HasTraitInfo<SelectableInfo>() && (x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x)));
x.Info.HasTraitInfo<ISelectableInfo>() && (x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x)));
if (!selectableActor || uog.InputOverridesSelection(World, mousePos, mi))
{
@@ -153,7 +154,7 @@ namespace OpenRA.Mods.Common.Widgets
if (unit != null && eligiblePlayers.Contains(unit.Owner))
{
var s = unit.TraitOrDefault<Selectable>();
var s = unit.TraitOrDefault<ISelectable>();
if (s != null)
{
// Select actors on the screen that have the same selection class as the actor under the mouse cursor
@@ -305,7 +306,7 @@ namespace OpenRA.Mods.Common.Widgets
// Get all the selected actors' selection classes
var selectedClasses = ownedActors
.Select(a => a.Trait<Selectable>().Class)
.Select(a => a.Trait<ISelectable>().Class)
.ToHashSet();
// Select actors on the screen that have the same selection class as one of the already selected actors
@@ -346,7 +347,7 @@ namespace OpenRA.Mods.Common.Widgets
if (!owners.Contains(a.Owner))
return false;
var s = a.TraitOrDefault<Selectable>();
var s = a.TraitOrDefault<ISelectable>();
// selectionClasses == null means that units, that meet all other criteria, get selected
return s != null && (selectionClasses == null || selectionClasses.Contains(s.Class));
@@ -356,7 +357,7 @@ namespace OpenRA.Mods.Common.Widgets
static IEnumerable<Actor> SelectHighestPriorityActorAtPoint(World world, int2 a, Modifiers modifiers)
{
var selected = world.ScreenMap.ActorsAtMouse(a)
.Where(x => x.Actor.Info.HasTraitInfo<SelectableInfo>() && (x.Actor.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x.Actor)))
.Where(x => x.Actor.Info.HasTraitInfo<ISelectableInfo>() && (x.Actor.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x.Actor)))
.WithHighestSelectionPriority(a, modifiers);
if (selected != null)
@@ -374,7 +375,7 @@ namespace OpenRA.Mods.Common.Widgets
return world.ScreenMap.ActorsInMouseBox(a, b)
.Select(x => x.Actor)
.Where(x => x.Info.HasTraitInfo<SelectableInfo>() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x)))
.Where(x => x.Info.HasTraitInfo<ISelectableInfo>() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x)))
.SubsetWithHighestSelectionPriority(modifiers);
}
}