diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index fe2ac3a27d..54da1172f1 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -110,23 +110,6 @@ namespace OpenRA return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m)); } - public Order Order( int2 xy, MouseInput mi, Actor underCursor ) - { - if (Owner != World.LocalPlayer) - return null; - - if (!World.Map.IsInMap(xy.X, xy.Y)) - return null; - - if (Destroyed) - return null; - - return TraitsImplementing() - .OrderByDescending( x => x.OrderPriority( this, xy, mi, underCursor ) ) - .Select( x => x.IssueOrder( this, xy, mi, underCursor ) ) - .FirstOrDefault( x => x != null ); - } - public RectangleF GetBounds(bool useAltitude) { var si = Info.Traits.GetOrDefault(); diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index fc368eb89d..6602abffa9 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -178,7 +178,7 @@ namespace OpenRA LobbyInfoChanged(); } - public static void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */ + public static void IssueOrder( Order o ) { orderManager.IssueOrder( o ); } /* avoid exposing the OM to mod code */ public static event Action AfterGameStart = () => {}; diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 0e72be2991..f74d655f40 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -11,7 +11,8 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -using OpenRA.Traits; +using OpenRA.Traits; +using OpenRA.FileFormats; namespace OpenRA.Orders { @@ -25,17 +26,17 @@ namespace OpenRA.Orders .FirstOrDefault(); var orders = world.Selection.Actors - .Select(a => a.Order(xy, mi, underCursor)) + .Select(a => OrderForUnit(a, xy, mi, underCursor)) .Where(o => o != null) .ToArray(); - var actorsInvolved = orders.Select(o => o.Subject).Distinct(); + var actorsInvolved = orders.Select(o => o.self).Distinct(); if (actorsInvolved.Any()) yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, - string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray())); - - foreach (var o in orders) - yield return o; + string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray())); + + foreach( var o in orders ) + yield return CheckSameOrder( o.iot, o.trait.IssueOrder( o.self, o.iot, o.target ) ); } public void Tick( World world ) {} @@ -62,22 +63,89 @@ namespace OpenRA.Orders public string GetCursor( World world, int2 xy, MouseInput mi ) { - if (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any()) - { - var underCursor = world.FindUnitsAtMouse(mi.Location) - .Where(a => a.Info.Traits.Contains()) - .Any(); - - if (underCursor) - return "select"; - } + var underCursor = world.FindUnitsAtMouse(mi.Location) + .Where(a => a.Info.Traits.Contains()) + .OrderByDescending(a => a.Info.Traits.Contains() ? a.Info.Traits.Get().Priority : int.MinValue) + .FirstOrDefault(); - var c = Order(world, xy, mi) - .Select(o => o.Subject.TraitsImplementing() - .Select(pc => pc.CursorForOrder(o.Subject, o)).FirstOrDefault(a => a != null)) - .FirstOrDefault(a => a != null); - - return c ?? "default"; + if( mi.Modifiers.HasModifier( Modifiers.Shift ) || !world.Selection.Actors.Any() ) + if( underCursor != null ) + return "select"; + + var orders = world.Selection.Actors + .Select(a => OrderForUnit(a, xy, mi, underCursor)) + .Where(o => o != null) + .ToArray(); + + if( orders.Length == 0 ) return "default"; + + return orders[ 0 ].cursor ?? "default"; } + + static UnitOrderResult OrderForUnit( Actor self, int2 xy, MouseInput mi, Actor underCursor ) + { + if (self.Owner != self.World.LocalPlayer) + return null; + + if (!self.World.Map.IsInMap(xy.X, xy.Y)) + return null; + + if (self.Destroyed) + return null; + + //var old = self.TraitsImplementing() + // .OrderByDescending( x => x.OrderPriority( self, xy, mi, underCursor ) ) + // .Select( x => x.IssueOrder( self, xy, mi, underCursor ) ) + // .FirstOrDefault( x => x != null ); + //if( old != null ) + // return old; + + if( mi.Button == MouseButton.Right ) + { + var uim = self.World.WorldActor.Trait(); + foreach( var o in self.TraitsImplementing() + .SelectMany( trait => trait.Orders + .Select( x => new { Trait = trait, Order = x } ) ) + .OrderByDescending( x => x.Order.OrderPriority ) ) + { + var actorsAt = uim.GetUnitsAt( xy ).ToList(); + + string cursor = null; + if( o.Order.CanTargetUnit( self, underCursor, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) ) + return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromActor( underCursor ) ); + if( o.Order.CanTargetLocation( self, xy, actorsAt, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) ) + return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromCell( xy ) ); + } + } + + return null; + } + + static Order CheckSameOrder( IOrderTargeter iot, Order order ) + { + if( order == null && iot.OrderID != null ) + Game.Debug( "BUG: in order targeter - decided on {0} but then didn't order", iot.OrderID ); + else if( iot.OrderID != order.OrderString ) + Game.Debug( "BUG: in order targeter - decided on {0} but ordered {1}", iot.OrderID, order.OrderString ); + return order; + } + + class UnitOrderResult + { + public readonly Actor self; + public readonly IOrderTargeter iot; + public readonly IIssueOrder2 trait; + public readonly string cursor; + public readonly Target target; + + public UnitOrderResult( Actor self, IOrderTargeter iot, IIssueOrder2 trait, string cursor, Target target ) + { + this.self = self; + this.iot = iot; + this.trait = trait; + this.cursor = cursor; + this.target = target; + } + } } } diff --git a/OpenRA.Game/Traits/Mobile.cs b/OpenRA.Game/Traits/Mobile.cs index 836d6aee15..8c30a0164f 100644 --- a/OpenRA.Game/Traits/Mobile.cs +++ b/OpenRA.Game/Traits/Mobile.cs @@ -16,6 +16,7 @@ using OpenRA.Effects; using OpenRA.Traits.Activities; using OpenRA.FileFormats; using System.Diagnostics; +using OpenRA.Orders; namespace OpenRA.Traits { @@ -53,7 +54,7 @@ namespace OpenRA.Traits } } - public class Mobile : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice, IOccupySpace, IMove, IFacing, INudge + public class Mobile : IIssueOrder2, IResolveOrder, IOrderCursor, IOrderVoice, IOccupySpace, IMove, IFacing, INudge { public readonly Actor self; public readonly MobileInfo Info; @@ -131,21 +132,17 @@ namespace OpenRA.Traits self.CenterLocation = Util.CenterOfCell(fromCell); } - public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor) - { - // Force move takes precedence - return mi.Modifiers.HasModifier(Modifiers.Alt) ? int.MaxValue : 0; - } - - // Note: Returns a valid order even if the unit can't move to the target - public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) - { - if (Info.OnRails) return null; - - if (mi.Button == MouseButton.Left) return null; + public IEnumerable Orders { get { yield return new MoveOrderTargeter( Info ); } } - var type = (!self.World.LocalPlayer.Shroud.IsVisible(xy) || CanEnterCell(xy)) ? "Move" : "Move-Blocked"; - return new Order(type, self, xy, mi.Modifiers.HasModifier(Modifiers.Shift)); + // Note: Returns a valid order even if the unit can't move to the target + public Order IssueOrder( Actor self, IOrderTargeter order, Target target ) + { + if( order is MoveOrderTargeter ) + { + if( Info.OnRails ) return null; + return new Order( "Move", self, Util.CellContaining( target.CenterLocation ), false ); + } + return null; } public int2 NearestMoveableCell(int2 target) @@ -369,5 +366,31 @@ namespace OpenRA.Traits Log.Write("debug", "OnNudge #{0} refuses at {1}", self.ActorID, self.Location); } + + class MoveOrderTargeter : IOrderTargeter + { + readonly MobileInfo unitType; + + public MoveOrderTargeter( MobileInfo unitType ) + { + this.unitType = unitType; + } + + public string OrderID { get { return "Move"; } } + public int OrderPriority { get { return 4; } } + + public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor ) + { + return false; + } + + public bool CanTargetLocation( Actor self, int2 location, List actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor ) + { + cursor = "move"; + if( self.World.LocalPlayer.Shroud.IsVisible( location ) && !self.Trait().CanEnterCell( location ) ) + cursor = "move-blocked"; + return true; + } + } } } diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 0ba14d33bc..19e9efb8d9 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -38,6 +38,18 @@ namespace OpenRA.Traits Order IssueOrder( Actor self, int2 xy, MouseInput mi, Actor underCursor ); int OrderPriority( Actor self, int2 xy, MouseInput mi, Actor underCursor ); } + public interface IIssueOrder2 + { + IEnumerable Orders { get; } + Order IssueOrder( Actor self, IOrderTargeter order, Target target ); + } + public interface IOrderTargeter + { + string OrderID { get; } + int OrderPriority { get; } + bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor ); + bool CanTargetLocation( Actor self, int2 location, List actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor ); + } public interface IResolveOrder { void ResolveOrder(Actor self, Order order); } public interface IOrderCursor { string CursorForOrder(Actor self, Order order); } public interface IOrderVoice { string VoicePhraseForOrder(Actor self, Order order); } diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index a079142d52..5a2a1c164b 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -26,44 +26,35 @@ namespace OpenRA.Mods.RA public readonly string[] RearmBuildings = { "fix" }; } - class Minelayer : IIssueOrder, IResolveOrder, IOrderCursor, IPostRenderSelection + class Minelayer : IIssueOrder2, IResolveOrder, IPostRenderSelection { /* [Sync] when sync can cope with arrays! */ public int2[] minefield = null; [Sync] int2 minefieldStart; - public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor) + public IEnumerable Orders { - return 5; + get { yield return new BeginMinefieldOrderTargeter(); } } - - public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) - { - if (mi.Button == MouseButton.Right && underCursor == null && mi.Modifiers.HasModifier(Modifiers.Ctrl)) - return new Order("BeginMinefield", self, xy); + public Order IssueOrder( Actor self, IOrderTargeter order, Target target ) + { + if( order is BeginMinefieldOrderTargeter ) + { + var start = Util.CellContaining( target.CenterLocation ); + self.World.OrderGenerator = new MinefieldOrderGenerator( self, start ); + return new Order( "BeginMinefield", self, start ); + } return null; } - public string CursorForOrder(Actor self, Order order) - { - return (order.OrderString == "BeginMinefield") ? "ability" : null; - } - public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "BeginMinefield") - { - //minefieldStart = order.TargetLocation; - //if (self.Owner == self.World.LocalPlayer) - // self.World.OrderGenerator = new MinefieldOrderGenerator(self); - } + if( order.OrderString == "BeginMinefield" ) + minefieldStart = order.TargetLocation; if (order.OrderString == "PlaceMinefield") { - if (self.Owner == self.World.LocalPlayer) - self.World.CancelInputMode(); - var movement = self.Trait(); minefield = GetMinefieldCells(minefieldStart, order.TargetLocation, @@ -97,9 +88,10 @@ namespace OpenRA.Mods.RA class MinefieldOrderGenerator : IOrderGenerator { - Actor minelayer; + readonly Actor minelayer; + readonly int2 minefieldStart; - public MinefieldOrderGenerator(Actor self) { minelayer = self; } + public MinefieldOrderGenerator(Actor self, int2 xy ) { minelayer = self; minefieldStart = xy; } public IEnumerable Order(World world, int2 xy, MouseInput mi) { @@ -114,8 +106,11 @@ namespace OpenRA.Mods.RA ? a.Info.Traits.Get().Priority : int.MinValue) .FirstOrDefault(); - if (mi.Button == MouseButton.Right && underCursor == null) - yield return new Order("PlaceMinefield", minelayer, xy); + if( mi.Button == MouseButton.Right && underCursor == null ) + { + minelayer.World.CancelInputMode(); + yield return new Order( "PlaceMinefield", minelayer, xy ); + } } public void Tick(World world) @@ -132,7 +127,7 @@ namespace OpenRA.Mods.RA var ml = minelayer.Trait(); var movement = minelayer.Trait(); - var minefield = GetMinefieldCells(ml.minefieldStart, lastMousePos, minelayer.Info.Traits.Get().MinefieldDepth) + var minefield = GetMinefieldCells(minefieldStart, lastMousePos, minelayer.Info.Traits.Get().MinefieldDepth) .Where(p => movement.CanEnterCell(p)).ToArray(); world.WorldRenderer.DrawLocus(Color.Cyan, minefield); @@ -151,5 +146,22 @@ namespace OpenRA.Mods.RA if (minefield != null) self.World.WorldRenderer.DrawLocus(Color.Cyan, minefield); } + + class BeginMinefieldOrderTargeter : IOrderTargeter + { + public string OrderID { get { return "BeginMinefield"; } } + public int OrderPriority { get { return 5; } } + + public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor ) + { + return false; + } + + public bool CanTargetLocation( Actor self, int2 location, List actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor ) + { + cursor = "ability"; + return ( actorsAtLocation.Count == 0 && forceAttack ); + } + } } } diff --git a/OpenRA.Mods.RA/Transforms.cs b/OpenRA.Mods.RA/Transforms.cs index 81277f1ba8..d78ec9cdb8 100644 --- a/OpenRA.Mods.RA/Transforms.cs +++ b/OpenRA.Mods.RA/Transforms.cs @@ -12,6 +12,7 @@ using OpenRA.Mods.RA.Activities; using OpenRA.Traits; using OpenRA.Traits.Activities; using OpenRA.GameRules; +using System.Collections.Generic; namespace OpenRA.Mods.RA { @@ -27,7 +28,7 @@ namespace OpenRA.Mods.RA public virtual object Create(ActorInitializer init) { return new Transforms(this); } } - class Transforms : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice + class Transforms : IIssueOrder2, IResolveOrder, IOrderVoice { TransformsInfo Info; BuildingInfo bi; @@ -38,19 +39,6 @@ namespace OpenRA.Mods.RA bi = Rules.Info[info.IntoActor].Traits.GetOrDefault(); } - public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor) - { - return 5; - } - - public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) - { - if (mi.Button == MouseButton.Right && self == underCursor) - return new Order("DeployTransform", self); - - return null; - } - public string VoicePhraseForOrder(Actor self, Order order) { return (order.OrderString == "DeployTransform") ? "Move" : null; @@ -60,7 +48,17 @@ namespace OpenRA.Mods.RA { return (bi == null || self.World.CanPlaceBuilding(Info.IntoActor, bi, self.Location + Info.Offset, self)); } - + + public IEnumerable Orders { get { yield return new TransformOrderTargeter(); } } + + public Order IssueOrder( Actor self, IOrderTargeter order, Target target ) + { + if( order is TransformOrderTargeter ) + return new Order( "DeployTransform", self ); + + return null; + } + public void ResolveOrder( Actor self, Order order ) { if (order.OrderString == "DeployTransform") @@ -79,13 +77,29 @@ namespace OpenRA.Mods.RA self.QueueActivity(new Transform(self, Info.IntoActor, Info.Offset, Info.Facing, Info.TransformSounds)); } } - - public string CursorForOrder(Actor self, Order order) + + class TransformOrderTargeter : IOrderTargeter { - if (order.OrderString != "DeployTransform") - return null; - - return CanDeploy(self) ? "deploy" : "deploy-blocked"; + public string OrderID + { + get { return "DeployTransform"; } + } + + public int OrderPriority + { + get { return 5; } + } + + public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor ) + { + cursor = self.Trait().CanDeploy( self ) ? "deploy" : "deploy-blocked"; + return self == target; + } + + public bool CanTargetLocation( Actor self, int2 location, System.Collections.Generic.List actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor ) + { + return false; + } } } }