From 7b31f180462107cc9269e1671e23421f430ea54f Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 26 Nov 2015 18:25:39 +0000 Subject: [PATCH 1/4] Reduce duplication in UnitOrderGenerator. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 107 +++++++++++------------ 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 958c85d8b3..198b8fc0dd 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -17,34 +17,41 @@ namespace OpenRA.Orders { class UnitOrderGenerator : IOrderGenerator { - public IEnumerable Order(World world, CPos xy, MouseInput mi) + static Target TargetForInput(World world, CPos xy, MouseInput mi) { - var underCursor = world.ScreenMap.ActorsAt(mi) + var actor = world.ScreenMap.ActorsAt(mi) .Where(a => !world.FogObscures(a) && a.Info.HasTraitInfo()) .WithHighestSelectionPriority(); - Target target; - if (underCursor != null) - target = Target.FromActor(underCursor); - else - { - var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi) - .Where(a => a.Info.HasTraitInfo() && a.Visible && a.HasRenderables) - .WithHighestSelectionPriority(); - target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(world, xy); - } + if (actor != null) + return Target.FromActor(actor); + var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi) + .Where(a => a.Info.HasTraitInfo() && a.Visible && a.HasRenderables) + .WithHighestSelectionPriority(); + + if (frozen != null) + return Target.FromFrozenActor(frozen); + + return Target.FromCell(world, xy); + } + + public IEnumerable Order(World world, CPos xy, MouseInput mi) + { + var target = TargetForInput(world, xy, mi); var orders = world.Selection.Actors .Select(a => OrderForUnit(a, target, mi)) .Where(o => o != null) .ToList(); var actorsInvolved = orders.Select(o => o.Actor).Distinct(); - if (actorsInvolved.Any()) - yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false) - { - TargetString = actorsInvolved.Select(a => a.ActorID).JoinWith(",") - }; + if (!actorsInvolved.Any()) + yield break; + + yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false) + { + TargetString = actorsInvolved.Select(a => a.ActorID).JoinWith(",") + }; foreach (var o in orders) yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift))); @@ -57,26 +64,10 @@ namespace OpenRA.Orders public string GetCursor(World world, CPos xy, MouseInput mi) { var useSelect = false; - var underCursor = world.ScreenMap.ActorsAt(mi) - .Where(a => !world.FogObscures(a) && a.Info.HasTraitInfo()) - .WithHighestSelectionPriority(); - - if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) - { - if (underCursor.Info.HasTraitInfo()) - useSelect = true; - } - - Target target; - if (underCursor != null) - target = Target.FromActor(underCursor); - else - { - var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi) - .Where(a => a.Info.HasTraitInfo() && a.Visible && a.HasRenderables) - .WithHighestSelectionPriority(); - target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(world, xy); - } + var target = TargetForInput(world, xy, mi); + if (target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo() && + (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) + useSelect = true; var ordersWithCursor = world.Selection.Actors .Select(a => OrderForUnit(a, target, mi)) @@ -94,7 +85,6 @@ namespace OpenRA.Orders var underCursor = world.Selection.Actors.WithHighestSelectionPriority(); var o = OrderForUnit(underCursor, target, mi); - if (o != null && o.Order.OverrideSelection) return false; @@ -103,6 +93,9 @@ namespace OpenRA.Orders static UnitOrderResult OrderForUnit(Actor self, Target target, MouseInput mi) { + if (mi.Button != Game.Settings.Game.MouseButtonPreference.Action) + return null; + if (self.Owner != self.World.LocalPlayer) return null; @@ -112,27 +105,25 @@ namespace OpenRA.Orders if (self.Disposed || !target.IsValidFor(self)) return null; - if (mi.Button == Game.Settings.Game.MouseButtonPreference.Action) + var modifiers = TargetModifiers.None; + if (mi.Modifiers.HasModifier(Modifiers.Ctrl)) + modifiers |= TargetModifiers.ForceAttack; + if (mi.Modifiers.HasModifier(Modifiers.Shift)) + modifiers |= TargetModifiers.ForceQueue; + if (mi.Modifiers.HasModifier(Modifiers.Alt)) + modifiers |= TargetModifiers.ForceMove; + + var actorsAt = self.World.ActorMap.GetActorsAt(self.World.Map.CellContaining(target.CenterPosition)).ToList(); + + foreach (var o in self.TraitsImplementing() + .SelectMany(trait => trait.Orders + .Select(x => new { Trait = trait, Order = x })) + .OrderByDescending(x => x.Order.OrderPriority)) { - foreach (var o in self.TraitsImplementing() - .SelectMany(trait => trait.Orders - .Select(x => new { Trait = trait, Order = x })) - .OrderByDescending(x => x.Order.OrderPriority)) - { - var actorsAt = self.World.ActorMap.GetActorsAt(self.World.Map.CellContaining(target.CenterPosition)).ToList(); - - var modifiers = TargetModifiers.None; - if (mi.Modifiers.HasModifier(Modifiers.Ctrl)) - modifiers |= TargetModifiers.ForceAttack; - if (mi.Modifiers.HasModifier(Modifiers.Shift)) - modifiers |= TargetModifiers.ForceQueue; - if (mi.Modifiers.HasModifier(Modifiers.Alt)) - modifiers |= TargetModifiers.ForceMove; - - string cursor = null; - if (o.Order.CanTarget(self, target, actorsAt, ref modifiers, ref cursor)) - return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); - } + var localModifiers = modifiers; + string cursor = null; + if (o.Order.CanTarget(self, target, actorsAt, ref localModifiers, ref cursor)) + return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); } return null; From fb99a1f3c265bfc85ad2aa099291809c5845b650 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 26 Nov 2015 18:30:43 +0000 Subject: [PATCH 2/4] Remove ActorMap query for each actor. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 198b8fc0dd..02e7113d42 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -39,8 +39,9 @@ namespace OpenRA.Orders public IEnumerable Order(World world, CPos xy, MouseInput mi) { var target = TargetForInput(world, xy, mi); + var actorsAt = world.ActorMap.GetActorsAt(xy).ToList(); var orders = world.Selection.Actors - .Select(a => OrderForUnit(a, target, mi)) + .Select(a => OrderForUnit(a, target, actorsAt, mi)) .Where(o => o != null) .ToList(); @@ -65,12 +66,14 @@ namespace OpenRA.Orders { var useSelect = false; var target = TargetForInput(world, xy, mi); + var actorsAt = world.ActorMap.GetActorsAt(xy).ToList(); + if (target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo() && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) useSelect = true; var ordersWithCursor = world.Selection.Actors - .Select(a => OrderForUnit(a, target, mi)) + .Select(a => OrderForUnit(a, target, actorsAt, mi)) .Where(o => o != null && o.Cursor != null); var cursorOrder = ordersWithCursor.MaxByOrDefault(o => o.Order.OrderPriority); @@ -81,17 +84,23 @@ namespace OpenRA.Orders // Used for classic mouse orders, determines whether or not action at xy is move or select public static bool InputOverridesSelection(World world, int2 xy, MouseInput mi) { - var target = Target.FromActor(world.ScreenMap.ActorsAt(xy).WithHighestSelectionPriority()); + var actor = world.ScreenMap.ActorsAt(xy).WithHighestSelectionPriority(); + if (actor == null) + return true; + + var target = Target.FromActor(actor); + var cell = world.Map.CellContaining(target.CenterPosition); + var actorsAt = world.ActorMap.GetActorsAt(cell).ToList(); var underCursor = world.Selection.Actors.WithHighestSelectionPriority(); - var o = OrderForUnit(underCursor, target, mi); + var o = OrderForUnit(underCursor, target, actorsAt, mi); if (o != null && o.Order.OverrideSelection) return false; return true; } - static UnitOrderResult OrderForUnit(Actor self, Target target, MouseInput mi) + static UnitOrderResult OrderForUnit(Actor self, Target target, List actorsAt, MouseInput mi) { if (mi.Button != Game.Settings.Game.MouseButtonPreference.Action) return null; @@ -113,8 +122,6 @@ namespace OpenRA.Orders if (mi.Modifiers.HasModifier(Modifiers.Alt)) modifiers |= TargetModifiers.ForceMove; - var actorsAt = self.World.ActorMap.GetActorsAt(self.World.Map.CellContaining(target.CenterPosition)).ToList(); - foreach (var o in self.TraitsImplementing() .SelectMany(trait => trait.Orders .Select(x => new { Trait = trait, Order = x })) From 756bec69b347091f7c3e4db134e20b3cb8a3fe5c Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 26 Nov 2015 18:37:40 +0000 Subject: [PATCH 3/4] Fall back to target cell if there are no valid orders against the target actor. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 02e7113d42..9031bb9262 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -41,7 +41,7 @@ namespace OpenRA.Orders var target = TargetForInput(world, xy, mi); var actorsAt = world.ActorMap.GetActorsAt(xy).ToList(); var orders = world.Selection.Actors - .Select(a => OrderForUnit(a, target, actorsAt, mi)) + .Select(a => OrderForUnit(a, target, actorsAt, xy, mi)) .Where(o => o != null) .ToList(); @@ -73,7 +73,7 @@ namespace OpenRA.Orders useSelect = true; var ordersWithCursor = world.Selection.Actors - .Select(a => OrderForUnit(a, target, actorsAt, mi)) + .Select(a => OrderForUnit(a, target, actorsAt, xy, mi)) .Where(o => o != null && o.Cursor != null); var cursorOrder = ordersWithCursor.MaxByOrDefault(o => o.Order.OrderPriority); @@ -93,14 +93,19 @@ namespace OpenRA.Orders var actorsAt = world.ActorMap.GetActorsAt(cell).ToList(); var underCursor = world.Selection.Actors.WithHighestSelectionPriority(); - var o = OrderForUnit(underCursor, target, actorsAt, mi); + var o = OrderForUnit(underCursor, target, actorsAt, cell, mi); if (o != null && o.Order.OverrideSelection) return false; return true; } - static UnitOrderResult OrderForUnit(Actor self, Target target, List actorsAt, MouseInput mi) + /// + /// Returns the most appropriate order for a given actor and target. + /// First priority is given to orders that interact with the given actors. + /// Second priority is given to actors in the given cell. + /// + static UnitOrderResult OrderForUnit(Actor self, Target target, List actorsAt, CPos xy, MouseInput mi) { if (mi.Button != Game.Settings.Game.MouseButtonPreference.Action) return null; @@ -122,15 +127,22 @@ namespace OpenRA.Orders if (mi.Modifiers.HasModifier(Modifiers.Alt)) modifiers |= TargetModifiers.ForceMove; - foreach (var o in self.TraitsImplementing() - .SelectMany(trait => trait.Orders - .Select(x => new { Trait = trait, Order = x })) - .OrderByDescending(x => x.Order.OrderPriority)) + var orders = self.TraitsImplementing() + .SelectMany(trait => trait.Orders.Select(x => new { Trait = trait, Order = x })) + .OrderByDescending(x => x.Order.OrderPriority); + + for (var i = 0; i < 2; i++) { - var localModifiers = modifiers; - string cursor = null; - if (o.Order.CanTarget(self, target, actorsAt, ref localModifiers, ref cursor)) - return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); + foreach (var o in orders) + { + var localModifiers = modifiers; + string cursor = null; + if (o.Order.CanTarget(self, target, actorsAt, ref localModifiers, ref cursor)) + return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); + } + + // No valid orders, so check for orders against the cell + target = Target.FromCell(self.World, xy); } return null; From 373bfefc6c25dad0b8b3c91700c61fdea550ea23 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 26 Nov 2015 18:38:10 +0000 Subject: [PATCH 4/4] Only support move orders against terrain targets. --- OpenRA.Mods.Common/Traits/Mobile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 835acd1ab0..0265f81ca2 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -709,7 +709,7 @@ namespace OpenRA.Mods.Common.Traits public bool CanTarget(Actor self, Target target, List othersAtTarget, ref TargetModifiers modifiers, ref string cursor) { - if (rejectMove || !target.IsValidFor(self)) + if (rejectMove || target.Type != TargetType.Terrain) return false; var location = self.World.Map.CellContaining(target.CenterPosition);