From 59d22a2ff0db1aa6dec37bea87f6ea7fc171fda9 Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sat, 12 Dec 2015 15:36:22 +0100 Subject: [PATCH] Fix AttackMove not working properly under certain conditions This fixes the issue where AttackMove (and possibly other order modes) would not work properly when people were clicking their mouse buttons at the same time. A move order (the default order mode) would be issued instead. --- OpenRA.Game/Orders/GenericSelectTarget.cs | 35 +++++++-------- OpenRA.Game/Orders/UnitOrderGenerator.cs | 12 +++--- .../WorldInteractionControllerWidget.cs | 21 +++++++-- OpenRA.Mods.Common/Traits/Guard.cs | 43 +++++++------------ .../Widgets/WorldCommandWidget.cs | 3 +- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/OpenRA.Game/Orders/GenericSelectTarget.cs b/OpenRA.Game/Orders/GenericSelectTarget.cs index 3f987be462..71909a62af 100644 --- a/OpenRA.Game/Orders/GenericSelectTarget.cs +++ b/OpenRA.Game/Orders/GenericSelectTarget.cs @@ -13,19 +13,19 @@ using OpenRA.Graphics; namespace OpenRA.Orders { - public class GenericSelectTarget : IOrderGenerator + public class GenericSelectTarget : UnitOrderGenerator { - readonly IEnumerable subjects; - readonly string order; - readonly string cursor; - readonly MouseButton expectedButton; + protected readonly IEnumerable Subjects; + protected readonly string OrderName; + protected readonly string Cursor; + protected readonly MouseButton ExpectedButton; public GenericSelectTarget(IEnumerable subjects, string order, string cursor, MouseButton button) { - this.subjects = subjects; - this.order = order; - this.cursor = cursor; - expectedButton = button; + Subjects = subjects; + OrderName = order; + Cursor = cursor; + ExpectedButton = button; } public GenericSelectTarget(IEnumerable subjects, string order, string cursor) @@ -37,26 +37,23 @@ namespace OpenRA.Orders public GenericSelectTarget(Actor subject, string order, string cursor, MouseButton button) : this(new Actor[] { subject }, order, cursor, button) { } - public IEnumerable Order(World world, CPos xy, MouseInput mi) + public override IEnumerable Order(World world, CPos xy, MouseInput mi) { - if (mi.Button != expectedButton) + if (mi.Button != ExpectedButton) world.CancelInputMode(); return OrderInner(world, xy, mi); } - IEnumerable OrderInner(World world, CPos xy, MouseInput mi) + protected virtual IEnumerable OrderInner(World world, CPos xy, MouseInput mi) { - if (mi.Button == expectedButton && world.Map.Contains(xy)) + if (mi.Button == ExpectedButton && world.Map.Contains(xy)) { world.CancelInputMode(); - foreach (var subject in subjects) - yield return new Order(order, subject, false) { TargetLocation = xy }; + foreach (var subject in Subjects) + yield return new Order(OrderName, subject, false) { TargetLocation = xy }; } } - public virtual void Tick(World world) { } - public IEnumerable Render(WorldRenderer wr, World world) { yield break; } - public IEnumerable RenderAfterWorld(WorldRenderer wr, World world) { yield break; } - public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; } + public override string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? Cursor : "generic-blocked"; } } } diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 9031bb9262..4f6b3057e8 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -15,7 +15,7 @@ using OpenRA.Traits; namespace OpenRA.Orders { - class UnitOrderGenerator : IOrderGenerator + public class UnitOrderGenerator : IOrderGenerator { static Target TargetForInput(World world, CPos xy, MouseInput mi) { @@ -36,7 +36,7 @@ namespace OpenRA.Orders return Target.FromCell(world, xy); } - public IEnumerable Order(World world, CPos xy, MouseInput mi) + public virtual IEnumerable Order(World world, CPos xy, MouseInput mi) { var target = TargetForInput(world, xy, mi); var actorsAt = world.ActorMap.GetActorsAt(xy).ToList(); @@ -58,11 +58,11 @@ namespace OpenRA.Orders yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift))); } - public void Tick(World world) { } - public IEnumerable Render(WorldRenderer wr, World world) { yield break; } - public IEnumerable RenderAfterWorld(WorldRenderer wr, World world) { yield break; } + public virtual void Tick(World world) { } + public virtual IEnumerable Render(WorldRenderer wr, World world) { yield break; } + public virtual IEnumerable RenderAfterWorld(WorldRenderer wr, World world) { yield break; } - public string GetCursor(World world, CPos xy, MouseInput mi) + public virtual string GetCursor(World world, CPos xy, MouseInput mi) { var useSelect = false; var target = TargetForInput(world, xy, mi); diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index c581301a32..1928ff1557 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -120,12 +120,25 @@ namespace OpenRA.Widgets } } } - else if (dragStart.HasValue) + else { - // Select actors in the dragbox - var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart.Value, xy); - World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy); + /* The block below does three things: + // 1. Allows actor selection using a selection box regardless of input mode. + // 2. Allows actor deselection with a single click in the default input mode (UnitOrderGenerator). + // 3. Prevents units from getting deselected when exiting input modes (eg. AttackMove or Guard). + // + // We cannot check for UnitOrderGenerator here since it's the default order generator that gets activated in + // World.CancelInputMode. If we did check it, actor de-selection would not be possible by just clicking somewhere, + // only by dragging an empty selection box. + */ + if (dragStart.HasValue && (!(World.OrderGenerator is GenericSelectTarget) || hasBox)) + { + var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart.Value, xy); + World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy); + } } + + World.CancelInputMode(); } dragStart = dragEnd = null; diff --git a/OpenRA.Mods.Common/Traits/Guard.cs b/OpenRA.Mods.Common/Traits/Guard.cs index 9b3f02a3a7..7ca77f34a1 100644 --- a/OpenRA.Mods.Common/Traits/Guard.cs +++ b/OpenRA.Mods.Common/Traits/Guard.cs @@ -14,6 +14,7 @@ using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common; using OpenRA.Mods.Common.Activities; +using OpenRA.Orders; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -59,52 +60,40 @@ namespace OpenRA.Mods.Common.Traits } } - public class GuardOrderGenerator : IOrderGenerator + public class GuardOrderGenerator : GenericSelectTarget { - readonly IEnumerable subjects; + public GuardOrderGenerator(IEnumerable subjects, string order, string cursor, MouseButton button) + : base(subjects, order, cursor, button) { } - public GuardOrderGenerator(IEnumerable subjects) + protected override IEnumerable OrderInner(World world, CPos xy, MouseInput mi) { - this.subjects = subjects; - } - - public IEnumerable Order(World world, CPos xy, MouseInput mi) - { - if (mi.Button == Game.Settings.Game.MouseButtonPreference.Cancel) - { - world.CancelInputMode(); - yield break; - } - var target = FriendlyGuardableUnits(world, mi).FirstOrDefault(); - if (target == null || subjects.All(s => s.IsDead)) + if (target == null || Subjects.All(s => s.IsDead)) yield break; - foreach (var subject in subjects) + world.CancelInputMode(); + foreach (var subject in Subjects) if (subject != target) - yield return new Order("Guard", subject, false) { TargetActor = target }; + yield return new Order(OrderName, subject, false) { TargetActor = target }; } - public void Tick(World world) + public override void Tick(World world) { - if (subjects.All(s => s.IsDead || !s.Info.HasTraitInfo())) + if (Subjects.All(s => s.IsDead || !s.Info.HasTraitInfo())) world.CancelInputMode(); } - public IEnumerable Render(WorldRenderer wr, World world) { yield break; } - public IEnumerable RenderAfterWorld(WorldRenderer wr, World world) { yield break; } - - public string GetCursor(World world, CPos xy, MouseInput mi) + public override string GetCursor(World world, CPos xy, MouseInput mi) { - if (!subjects.Any()) + if (!Subjects.Any()) return null; - var multiple = subjects.Count() > 1; + var multiple = Subjects.Count() > 1; var canGuard = FriendlyGuardableUnits(world, mi) - .Any(a => multiple || a != subjects.First()); + .Any(a => multiple || a != Subjects.First()); - return canGuard ? "guard" : "move-blocked"; + return canGuard ? Cursor : "move-blocked"; } static IEnumerable FriendlyGuardableUnits(World world, MouseInput mi) diff --git a/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs b/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs index 10c96d78f9..402221167d 100644 --- a/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs +++ b/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs @@ -184,7 +184,8 @@ namespace OpenRA.Mods.Common.Widgets .Where(a => !a.Disposed && a.Owner == world.LocalPlayer && a.Info.HasTraitInfo()); if (actors.Any()) - world.OrderGenerator = new GuardOrderGenerator(actors); + world.OrderGenerator = new GuardOrderGenerator(actors, + "Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action); return true; }