From c1fc0c1b746efc8dbf4ff77ce3b073f4bf076d12 Mon Sep 17 00:00:00 2001 From: Ivaylo Draganov Date: Wed, 8 May 2019 18:12:00 +0300 Subject: [PATCH] Allow selection priority to be modified using a hotkey --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 8 ++-- OpenRA.Game/SelectableExts.cs | 41 +++++++++++++------ OpenRA.Game/Traits/Selectable.cs | 18 +++++++- .../Widgets/ViewportControllerWidget.cs | 5 ++- .../WorldInteractionControllerWidget.cs | 23 ++++++----- 5 files changed, 64 insertions(+), 31 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 9e11f0802c..1a3df73705 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -22,14 +22,14 @@ namespace OpenRA.Orders { var actor = world.ScreenMap.ActorsAtMouse(mi) .Where(a => !a.Actor.IsDead && a.Actor.Info.HasTraitInfo() && !world.FogObscures(a.Actor)) - .WithHighestSelectionPriority(worldPixel); + .WithHighestSelectionPriority(worldPixel, mi.Modifiers); if (actor != null) return Target.FromActor(actor); var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, mi) .Where(a => a.Info.HasTraitInfo() && a.Visible && a.HasRenderables) - .WithHighestSelectionPriority(worldPixel); + .WithHighestSelectionPriority(worldPixel, mi.Modifiers); if (frozen != null) return Target.FromFrozenActor(frozen); @@ -93,7 +93,7 @@ namespace OpenRA.Orders { var actor = world.ScreenMap.ActorsAtMouse(xy) .Where(a => !a.Actor.IsDead) - .WithHighestSelectionPriority(xy); + .WithHighestSelectionPriority(xy, mi.Modifiers); if (actor == null) return true; @@ -103,7 +103,7 @@ namespace OpenRA.Orders var actorsAt = world.ActorMap.GetActorsAt(cell).ToList(); var underCursor = world.Selection.Actors .Select(a => new ActorBoundsPair(a, a.MouseBounds(wr))) - .WithHighestSelectionPriority(xy); + .WithHighestSelectionPriority(xy, mi.Modifiers); var o = OrderForUnit(underCursor, target, actorsAt, cell, mi); if (o != null) diff --git a/OpenRA.Game/SelectableExts.cs b/OpenRA.Game/SelectableExts.cs index 17c738359b..3fb7242078 100644 --- a/OpenRA.Game/SelectableExts.cs +++ b/OpenRA.Game/SelectableExts.cs @@ -18,17 +18,19 @@ namespace OpenRA.Traits { public static class SelectableExts { - public static int SelectionPriority(this ActorInfo a) + public static int SelectionPriority(this ActorInfo a, Modifiers modifiers) { var selectableInfo = a.TraitInfoOrDefault(); - return selectableInfo != null ? selectableInfo.Priority : int.MinValue; + return selectableInfo != null ? BaseSelectionPriority(selectableInfo, modifiers) : int.MinValue; } const int PriorityRange = 30; - public static int SelectionPriority(this Actor a) + public static int SelectionPriority(this Actor a, Modifiers modifiers) { - var basePriority = a.Info.TraitInfo().Priority; + var info = a.Info.TraitInfo(); + var basePriority = BaseSelectionPriority(info, modifiers); + var lp = a.World.LocalPlayer; if (a.Owner == lp || lp == null) @@ -45,37 +47,50 @@ namespace OpenRA.Traits } } - public static Actor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) + static int BaseSelectionPriority(SelectableInfo info, Modifiers modifiers) + { + var priority = info.Priority; + + if (modifiers.HasModifier(Modifiers.Ctrl) && !modifiers.HasModifier(Modifiers.Alt) && info.PriorityModifiers.HasFlag(SelectionPriorityModifiers.Ctrl)) + priority = int.MaxValue; + + if (modifiers.HasModifier(Modifiers.Alt) && !modifiers.HasModifier(Modifiers.Ctrl) && info.PriorityModifiers.HasFlag(SelectionPriorityModifiers.Alt)) + priority = int.MaxValue; + + return priority; + } + + public static Actor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel, Modifiers modifiers) { if (!actors.Any()) return null; - return actors.MaxBy(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel)).Actor; + return actors.MaxBy(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel, modifiers)).Actor; } - public static FrozenActor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) + public static FrozenActor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel, Modifiers modifiers) { - return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.MouseBounds, selectionPixel)); + return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.MouseBounds, selectionPixel, modifiers)); } - static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel) + static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel, Modifiers modifiers) { if (bounds.IsEmpty) - return info.SelectionPriority(); + return info.SelectionPriority(modifiers); var centerPixel = new int2( bounds.Left + bounds.Size.Width / 2, bounds.Top + bounds.Size.Height / 2); var pixelDistance = (centerPixel - selectionPixel).Length; - return ((long)-pixelDistance << 32) + info.SelectionPriority(); + return ((long)-pixelDistance << 32) + info.SelectionPriority(modifiers); } static readonly Actor[] NoActors = { }; - public static IEnumerable SubsetWithHighestSelectionPriority(this IEnumerable actors) + public static IEnumerable SubsetWithHighestSelectionPriority(this IEnumerable actors, Modifiers modifiers) { - return actors.GroupBy(x => x.SelectionPriority()) + return actors.GroupBy(x => x.SelectionPriority(modifiers)) .OrderByDescending(g => g.Key) .Select(g => g.AsEnumerable()) .DefaultIfEmpty(NoActors) diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 617d662985..9387fcb8ea 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -9,13 +9,29 @@ */ #endregion +using System; + namespace OpenRA.Traits { - [Desc("This actor is selectable. Defines bounds of selectable area, selection class and selection priority.")] + [Flags] + public enum SelectionPriorityModifiers + { + None = 0, + Ctrl = 1, + Alt = 2 + } + + [Desc("This actor is selectable. Defines bounds of selectable area, selection class, selection priority and selection priority modifiers.")] public class SelectableInfo : InteractableInfo { 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; diff --git a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs index 4857738403..07825910bf 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -218,6 +218,7 @@ namespace OpenRA.Mods.Common.Widgets { TooltipType = WorldTooltipType.None; ActorTooltipExtra = null; + var modifiers = Game.GetModifierKeys(); var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); if (!world.Map.Contains(cell)) return; @@ -231,7 +232,7 @@ namespace OpenRA.Mods.Common.Widgets var worldPixel = worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos); var underCursor = world.ScreenMap.ActorsAtMouse(worldPixel) .Where(a => a.Actor.Info.HasTraitInfo() && !world.FogObscures(a.Actor)) - .WithHighestSelectionPriority(worldPixel); + .WithHighestSelectionPriority(worldPixel, modifiers); if (underCursor != null) { @@ -247,7 +248,7 @@ namespace OpenRA.Mods.Common.Widgets var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, worldPixel) .Where(a => a.TooltipInfo != null && a.IsValid && a.Visible && !a.Hidden) - .WithHighestSelectionPriority(worldPixel); + .WithHighestSelectionPriority(worldPixel, modifiers); if (frozen != null) { diff --git a/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs index 601780172e..9060fad289 100644 --- a/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs @@ -56,6 +56,7 @@ namespace OpenRA.Mods.Common.Widgets public override void Draw() { + var modifiers = Game.GetModifierKeys(); if (IsValidDragbox) { // Render actors in the dragbox @@ -63,13 +64,13 @@ namespace OpenRA.Mods.Common.Widgets var b = new float3(mousePos.X, mousePos.Y, mousePos.Y); Game.Renderer.WorldRgbaColorRenderer.DrawRect(a, b, 1 / worldRenderer.Viewport.Zoom, Color.White); - foreach (var u in SelectActorsInBoxWithDeadzone(World, dragStart, mousePos)) + foreach (var u in SelectActorsInBoxWithDeadzone(World, dragStart, mousePos, modifiers)) DrawRollover(u); } else { // Render actors under the mouse pointer - foreach (var u in SelectActorsInBoxWithDeadzone(World, mousePos, mousePos)) + foreach (var u in SelectActorsInBoxWithDeadzone(World, mousePos, mousePos, modifiers)) DrawRollover(u); } } @@ -125,7 +126,7 @@ namespace OpenRA.Mods.Common.Widgets if (multiClick) { var unit = World.ScreenMap.ActorsAtMouse(mousePos) - .WithHighestSelectionPriority(mousePos); + .WithHighestSelectionPriority(mousePos, mi.Modifiers); if (unit != null && unit.Owner == (World.RenderPlayer ?? World.LocalPlayer)) { @@ -152,7 +153,7 @@ namespace OpenRA.Mods.Common.Widgets */ if (isDragging && (!(World.OrderGenerator is GenericSelectTarget) || IsValidDragbox)) { - var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart, mousePos); + var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart, mousePos, mi.Modifiers); World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == mousePos); } } @@ -251,7 +252,7 @@ namespace OpenRA.Mods.Common.Widgets if (SelectAllKey.IsActivatedBy(e) && !World.IsGameOver) { // Select actors on the screen which belong to the current player - var ownUnitsOnScreen = SelectActorsOnScreen(World, worldRenderer, null, player).SubsetWithHighestSelectionPriority().ToList(); + var ownUnitsOnScreen = SelectActorsOnScreen(World, worldRenderer, null, player).SubsetWithHighestSelectionPriority(e.Modifiers).ToList(); // Check if selecting actors on the screen has selected new units if (ownUnitsOnScreen.Count > World.Selection.Actors.Count()) @@ -259,7 +260,7 @@ namespace OpenRA.Mods.Common.Widgets else { // Select actors in the world that have highest selection priority - ownUnitsOnScreen = SelectActorsInWorld(World, null, player).SubsetWithHighestSelectionPriority().ToList(); + ownUnitsOnScreen = SelectActorsInWorld(World, null, player).SubsetWithHighestSelectionPriority(e.Modifiers).ToList(); Game.AddSystemLine("Battlefield Control", "Selected across map"); } @@ -321,29 +322,29 @@ namespace OpenRA.Mods.Common.Widgets }); } - static IEnumerable SelectHighestPriorityActorAtPoint(World world, int2 a) + static IEnumerable SelectHighestPriorityActorAtPoint(World world, int2 a, Modifiers modifiers) { var selected = world.ScreenMap.ActorsAtMouse(a) .Where(x => x.Actor.Info.HasTraitInfo() && (x.Actor.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x.Actor))) - .WithHighestSelectionPriority(a); + .WithHighestSelectionPriority(a, modifiers); if (selected != null) yield return selected; } - static IEnumerable SelectActorsInBoxWithDeadzone(World world, int2 a, int2 b) + static IEnumerable SelectActorsInBoxWithDeadzone(World world, int2 a, int2 b, Modifiers modifiers) { // 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; if (a == b) - return SelectHighestPriorityActorAtPoint(world, a); + return SelectHighestPriorityActorAtPoint(world, a, modifiers); return world.ScreenMap.ActorsInMouseBox(a, b) .Select(x => x.Actor) .Where(x => x.Info.HasTraitInfo() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x))) - .SubsetWithHighestSelectionPriority(); + .SubsetWithHighestSelectionPriority(modifiers); } } }