Touched up selection functionality.
This commit is contained in:
@@ -21,11 +21,17 @@ namespace OpenRA.Traits
|
|||||||
public readonly int Priority = 10;
|
public readonly int Priority = 10;
|
||||||
public readonly int[] Bounds = null;
|
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). "
|
||||||
|
+ "Defaults to the actor name when not defined or inherited.")]
|
||||||
|
public readonly string Class = null;
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
|
public object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Selectable : IPostRenderSelection
|
public class Selectable : IPostRenderSelection
|
||||||
{
|
{
|
||||||
|
public readonly string Class = null;
|
||||||
|
|
||||||
public SelectableInfo Info;
|
public SelectableInfo Info;
|
||||||
readonly Actor self;
|
readonly Actor self;
|
||||||
|
|
||||||
@@ -33,6 +39,7 @@ namespace OpenRA.Traits
|
|||||||
{
|
{
|
||||||
this.self = self;
|
this.self = self;
|
||||||
Info = info;
|
Info = info;
|
||||||
|
Class = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<WPos> ActivityTargetPath()
|
IEnumerable<WPos> ActivityTargetPath()
|
||||||
|
|||||||
@@ -40,15 +40,17 @@ namespace OpenRA.Widgets
|
|||||||
{
|
{
|
||||||
if (!IsDragging)
|
if (!IsDragging)
|
||||||
{
|
{
|
||||||
foreach (var u in SelectActorsInBoxWithDeadzone(World, lastMousePosition, lastMousePosition, _ => true))
|
// Render actors under the mouse pointer
|
||||||
|
foreach (var u in SelectActorsInBoxWithDeadzone(World, lastMousePosition, lastMousePosition))
|
||||||
worldRenderer.DrawRollover(u);
|
worldRenderer.DrawRollover(u);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render actors in the dragbox
|
||||||
var selbox = SelectionBox;
|
var selbox = SelectionBox;
|
||||||
Game.Renderer.WorldLineRenderer.DrawRect(selbox.Value.First.ToFloat2(), selbox.Value.Second.ToFloat2(), Color.White);
|
Game.Renderer.WorldLineRenderer.DrawRect(selbox.Value.First.ToFloat2(), selbox.Value.Second.ToFloat2(), Color.White);
|
||||||
foreach (var u in SelectActorsInBoxWithDeadzone(World, selbox.Value.First, selbox.Value.Second, _ => true))
|
foreach (var u in SelectActorsInBoxWithDeadzone(World, selbox.Value.First, selbox.Value.Second))
|
||||||
worldRenderer.DrawRollover(u);
|
worldRenderer.DrawRollover(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,15 +113,20 @@ namespace OpenRA.Widgets
|
|||||||
|
|
||||||
if (unit != null && unit.Owner == (World.RenderPlayer ?? World.LocalPlayer))
|
if (unit != null && unit.Owner == (World.RenderPlayer ?? World.LocalPlayer))
|
||||||
{
|
{
|
||||||
var newSelection2 = SelectActorsInBox(World, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight,
|
var s = unit.TraitOrDefault<Selectable>();
|
||||||
a => a.Owner == unit.Owner && a.Info.Name == unit.Info.Name);
|
if (s != null)
|
||||||
|
{
|
||||||
|
// Select actors on the screen that have the same selection class as the actor under the mouse cursor
|
||||||
|
var newSelection = SelectActorsOnScreen(World, worldRenderer, new HashSet<string> { s.Class }, unit.Owner);
|
||||||
|
|
||||||
World.Selection.Combine(World, newSelection2, true, false);
|
World.Selection.Combine(World, newSelection, true, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dragStart.HasValue)
|
else if (dragStart.HasValue)
|
||||||
{
|
{
|
||||||
var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart.Value, xy, _ => true);
|
// Select actors in the dragbox
|
||||||
|
var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart.Value, xy);
|
||||||
World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy);
|
World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,26 +237,28 @@ namespace OpenRA.Widgets
|
|||||||
World.SetPauseState(!World.Paused);
|
World.SetPauseState(!World.Paused);
|
||||||
else if (key == Game.Settings.Keys.SelectAllUnitsKey)
|
else if (key == Game.Settings.Keys.SelectAllUnitsKey)
|
||||||
{
|
{
|
||||||
var ownUnitsOnScreen = SelectActorsInBox(World, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight,
|
// Select actors on the screen which belong to the current player
|
||||||
a => a.Owner == player);
|
var ownUnitsOnScreen = SelectActorsOnScreen(World, worldRenderer, null, player);
|
||||||
World.Selection.Combine(World, ownUnitsOnScreen, false, false);
|
World.Selection.Combine(World, ownUnitsOnScreen, false, false);
|
||||||
}
|
}
|
||||||
else if (key == Game.Settings.Keys.SelectUnitsByTypeKey)
|
else if (key == Game.Settings.Keys.SelectUnitsByTypeKey)
|
||||||
{
|
{
|
||||||
var selectedTypes = World.Selection.Actors
|
// Get all the selected actors' selection classes
|
||||||
|
var selectedClasses = World.Selection.Actors
|
||||||
.Where(x => x.Owner == player)
|
.Where(x => x.Owner == player)
|
||||||
.Select(a => a.Info);
|
.Select(a => a.Trait<Selectable>().Class)
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
Func<Actor, bool> cond = a => a.Owner == player && selectedTypes.Contains(a.Info);
|
// Select actors on the screen that have the same selection class as one of the already selected actors
|
||||||
var tl = worldRenderer.Viewport.TopLeft;
|
var newSelection = SelectActorsOnScreen(World, worldRenderer, selectedClasses, player).ToList();
|
||||||
var br = worldRenderer.Viewport.BottomRight;
|
|
||||||
var newSelection = SelectActorsInBox(World, tl, br, cond);
|
|
||||||
|
|
||||||
if (newSelection.Count() > selectedTypes.Count())
|
// Check if selecting actors on the screen has selected new units
|
||||||
|
if (newSelection.Count() > World.Selection.Actors.Count())
|
||||||
Game.Debug("Selected across screen");
|
Game.Debug("Selected across screen");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newSelection = World.ActorMap.ActorsInWorld().Where(cond);
|
// Select actors in the world that have the same selection class as one of the already selected actors
|
||||||
|
newSelection = SelectActorsInWorld(World, selectedClasses, player).ToList();
|
||||||
Game.Debug("Selected across map");
|
Game.Debug("Selected across map");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,18 +273,41 @@ namespace OpenRA.Widgets
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IEnumerable<Actor> SelectActorsInBoxWithDeadzone(World world, int2 a, int2 b, Func<Actor, bool> cond)
|
static IEnumerable<Actor> SelectActorsOnScreen(World world, WorldRenderer wr, IEnumerable<string> selectionClasses, Player player)
|
||||||
{
|
{
|
||||||
if (a == b || (a - b).Length > Game.Settings.Game.SelectionDeadzone)
|
return SelectActorsByPlayerByClass(world.ScreenMap.ActorsInBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight), selectionClasses, player);
|
||||||
return SelectActorsInBox(world, a, b, cond);
|
|
||||||
else
|
|
||||||
return SelectActorsInBox(world, b, b, cond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static IEnumerable<Actor> SelectActorsInBox(World world, int2 a, int2 b, Func<Actor, bool> cond)
|
static IEnumerable<Actor> SelectActorsInWorld(World world, IEnumerable<string> selectionClasses, Player player)
|
||||||
{
|
{
|
||||||
|
return SelectActorsByPlayerByClass(world.ActorMap.ActorsInWorld(), selectionClasses, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
static IEnumerable<Actor> SelectActorsByPlayerByClass(IEnumerable<Actor> actors, IEnumerable<string> selectionClasses, Player player)
|
||||||
|
{
|
||||||
|
return actors.Where(a =>
|
||||||
|
{
|
||||||
|
if (a.Owner != player)
|
||||||
|
return false;
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static IEnumerable<Actor> SelectActorsInBoxWithDeadzone(World world, int2 a, int2 b)
|
||||||
|
{
|
||||||
|
// For dragboxes that are too small, shrink the dragbox to a single point (point b)
|
||||||
|
if ((a - b).Length <= Game.Settings.Game.SelectionDeadzone)
|
||||||
|
a = b;
|
||||||
|
|
||||||
return world.ScreenMap.ActorsInBox(a, b)
|
return world.ScreenMap.ActorsInBox(a, b)
|
||||||
.Where(x => x.HasTrait<Selectable>() && x.Trait<Selectable>().Info.Selectable && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x)) && cond(x))
|
.Where(x =>
|
||||||
|
{
|
||||||
|
var s = x.TraitOrDefault<Selectable>();
|
||||||
|
return s != null && s.Info.Selectable && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x));
|
||||||
|
})
|
||||||
.GroupBy(x => x.GetSelectionPriority())
|
.GroupBy(x => x.GetSelectionPriority())
|
||||||
.OrderByDescending(g => g.Key)
|
.OrderByDescending(g => g.Key)
|
||||||
.Select(g => g.AsEnumerable())
|
.Select(g => g.AsEnumerable())
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ trike.starport:
|
|||||||
Inherits: trike
|
Inherits: trike
|
||||||
Buildable:
|
Buildable:
|
||||||
Queue: Starport
|
Queue: Starport
|
||||||
Prerequisites: starport
|
|
||||||
Valued:
|
Valued:
|
||||||
Cost: 315
|
Cost: 315
|
||||||
WithFacingSpriteBody:
|
WithFacingSpriteBody:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ mcv:
|
|||||||
Name: Mobile Construction Vehicle
|
Name: Mobile Construction Vehicle
|
||||||
Description: Deploys into another Construction Yard\n Unarmed
|
Description: Deploys into another Construction Yard\n Unarmed
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: mcv
|
||||||
Priority: 3
|
Priority: 3
|
||||||
Bounds: 42,42
|
Bounds: 42,42
|
||||||
Health:
|
Health:
|
||||||
@@ -51,6 +52,7 @@ harvester:
|
|||||||
Name: Spice Harvester
|
Name: Spice Harvester
|
||||||
Description: Collects Spice for processing\n Unarmed
|
Description: Collects Spice for processing\n Unarmed
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: harvester
|
||||||
Priority: 7
|
Priority: 7
|
||||||
Bounds: 42,42
|
Bounds: 42,42
|
||||||
Harvester:
|
Harvester:
|
||||||
@@ -96,6 +98,7 @@ trike:
|
|||||||
Name: Scout Trike
|
Name: Scout Trike
|
||||||
Description: Fast Scout\n Strong vs Infantry\n Weak vs Tanks, Aircraft
|
Description: Fast Scout\n Strong vs Infantry\n Weak vs Tanks, Aircraft
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: trike
|
||||||
Bounds: 24,24
|
Bounds: 24,24
|
||||||
Health:
|
Health:
|
||||||
HP: 100
|
HP: 100
|
||||||
@@ -151,6 +154,7 @@ quad:
|
|||||||
Weapon: UnitExplodeTiny
|
Weapon: UnitExplodeTiny
|
||||||
EmptyWeapon: UnitExplodeTiny
|
EmptyWeapon: UnitExplodeTiny
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: quad
|
||||||
Bounds: 24,24
|
Bounds: 24,24
|
||||||
AttractsWorms:
|
AttractsWorms:
|
||||||
Intensity: 470
|
Intensity: 470
|
||||||
@@ -195,6 +199,7 @@ siegetank:
|
|||||||
AutoTarget:
|
AutoTarget:
|
||||||
InitialStance: Defend
|
InitialStance: Defend
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: siegetank
|
||||||
Bounds: 30,30
|
Bounds: 30,30
|
||||||
LeavesHusk:
|
LeavesHusk:
|
||||||
HuskActor: siegetank.husk
|
HuskActor: siegetank.husk
|
||||||
@@ -235,6 +240,7 @@ missiletank:
|
|||||||
Weapon: UnitExplodeScale
|
Weapon: UnitExplodeScale
|
||||||
EmptyWeapon: UnitExplodeScale
|
EmptyWeapon: UnitExplodeScale
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: missiletank
|
||||||
Bounds: 30,30
|
Bounds: 30,30
|
||||||
LeavesHusk:
|
LeavesHusk:
|
||||||
HuskActor: missiletank.husk
|
HuskActor: missiletank.husk
|
||||||
@@ -449,6 +455,7 @@ deviatortank:
|
|||||||
Weapon: UnitExplodeSmall
|
Weapon: UnitExplodeSmall
|
||||||
EmptyWeapon: UnitExplodeSmall
|
EmptyWeapon: UnitExplodeSmall
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: combat
|
||||||
Bounds: 30,30
|
Bounds: 30,30
|
||||||
AttractsWorms:
|
AttractsWorms:
|
||||||
Intensity: 520
|
Intensity: 520
|
||||||
|
|||||||
@@ -462,6 +462,8 @@
|
|||||||
|
|
||||||
^CivInfantry:
|
^CivInfantry:
|
||||||
Inherits: ^Infantry
|
Inherits: ^Infantry
|
||||||
|
Selectable:
|
||||||
|
Class: CivInfantry
|
||||||
Valued:
|
Valued:
|
||||||
Cost: 70
|
Cost: 70
|
||||||
Tooltip:
|
Tooltip:
|
||||||
|
|||||||
@@ -388,6 +388,8 @@ DELPHI:
|
|||||||
|
|
||||||
CHAN:
|
CHAN:
|
||||||
Inherits: ^CivInfantry
|
Inherits: ^CivInfantry
|
||||||
|
Selectable:
|
||||||
|
Class: CHAN
|
||||||
Tooltip:
|
Tooltip:
|
||||||
Name: Agent Chan
|
Name: Agent Chan
|
||||||
|
|
||||||
@@ -396,6 +398,7 @@ GNRL:
|
|||||||
Tooltip:
|
Tooltip:
|
||||||
Name: General
|
Name: General
|
||||||
Selectable:
|
Selectable:
|
||||||
|
Class: GNRL
|
||||||
Voiced:
|
Voiced:
|
||||||
VoiceSet: StavrosVoice
|
VoiceSet: StavrosVoice
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user