diff --git a/OpenRa.Game/Actor.cs b/OpenRa.Game/Actor.cs index 68dd454762..e6596ac5f7 100755 --- a/OpenRa.Game/Actor.cs +++ b/OpenRa.Game/Actor.cs @@ -83,7 +83,7 @@ namespace OpenRa.Game var underCursor = Game.UnitInfluence.GetUnitAt( xy ) ?? Game.BuildingInfluence.GetBuildingAt( xy ); return traits.WithInterface() - .Select( x => x.Order( this, xy, lmb, underCursor ) ) + .Select( x => x.IssueOrder( this, xy, lmb, underCursor ) ) .FirstOrDefault( x => x != null ); } @@ -149,10 +149,10 @@ namespace OpenRa.Game act.NextActivity = nextActivity; } - public void CancelActivity( Actor self ) + public void CancelActivity() { if( currentActivity != null ) - currentActivity.Cancel( self ); + currentActivity.Cancel( this ); } // For pathdebug, et al diff --git a/OpenRa.Game/Order.cs b/OpenRa.Game/Order.cs index c66f2387ef..4bea2505b6 100644 --- a/OpenRa.Game/Order.cs +++ b/OpenRa.Game/Order.cs @@ -85,6 +85,8 @@ namespace OpenRa.Game return Game.world.Actors.Where(x => x.ActorID == aID).First(); } + // Named constructors for Orders. + // Now that Orders are resolved by individual Actors, these are weird; you unpack orders manually, but not pack them. public static Order Chat(Player subject, string text) { return new Order(subject, "Chat", null, null, int2.Zero, text); diff --git a/OpenRa.Game/Traits/AttackTurreted.cs b/OpenRa.Game/Traits/AttackTurreted.cs index 69903afbac..f498f6f64d 100755 --- a/OpenRa.Game/Traits/AttackTurreted.cs +++ b/OpenRa.Game/Traits/AttackTurreted.cs @@ -69,11 +69,30 @@ namespace OpenRa.Game.Traits return true; } - public Order Order( Actor self, int2 xy, bool lmb, Actor underCursor ) + public Order IssueOrder( Actor self, int2 xy, bool lmb, Actor underCursor ) { if( lmb || underCursor == null ) return null; if( underCursor.Owner == self.Owner ) return null; - return OpenRa.Game.Order.Attack( self, underCursor ); + return Order.Attack( self, underCursor ); + } + + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "Attack" ) + { + self.CancelActivity(); + QueueAttack( self, order ); + } + } + + protected virtual void QueueAttack( Actor self, Order order ) + { + const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ + /* todo: choose the appropriate weapon, when only one works against this target */ + var weapon = order.Subject.unitInfo.Primary ?? order.Subject.unitInfo.Secondary; + + self.QueueActivity( new Traits.Activities.Attack( order.TargetActor, + Math.Max( 0, (int)Rules.WeaponInfo[ weapon ].Range - RangeTolerance ) ) ); } } @@ -94,5 +113,16 @@ namespace OpenRa.Game.Traits DoAttack( self ); } + + protected override void QueueAttack( Actor self, Order order ) + { + const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ + /* todo: choose the appropriate weapon, when only one works against this target */ + var weapon = order.Subject.unitInfo.Primary ?? order.Subject.unitInfo.Secondary; + + self.QueueActivity( new Traits.Activities.Follow( order.TargetActor, + Math.Max( 0, (int)Rules.WeaponInfo[ weapon ].Range - RangeTolerance ) ) ); + self.traits.Get().target = order.TargetActor; + } } } diff --git a/OpenRa.Game/Traits/Harvester.cs b/OpenRa.Game/Traits/Harvester.cs index 8e1abbe794..111d4b0ef1 100644 --- a/OpenRa.Game/Traits/Harvester.cs +++ b/OpenRa.Game/Traits/Harvester.cs @@ -13,6 +13,8 @@ namespace OpenRa.Game.Traits public bool IsFull { get { return oreCarried + gemsCarried == Rules.General.BailCount; } } public bool IsEmpty { get { return oreCarried == 0 && gemsCarried == 0; } } + public Harvester( Actor self ) { } + public void AcceptResource(bool isGem) { if (isGem) gemsCarried++; @@ -27,21 +29,34 @@ namespace OpenRa.Game.Traits gemsCarried = 0; } - public Order Order(Actor self, int2 xy, bool lmb, Actor underCursor) + public Order IssueOrder(Actor self, int2 xy, bool lmb, Actor underCursor) { if (lmb) return null; if (underCursor != null && underCursor.Owner == self.Owner && underCursor.traits.Contains() && !IsEmpty) - return OpenRa.Game.Order.DeliverOre(self, underCursor); + return Order.DeliverOre(self, underCursor); if (underCursor == null && Rules.Map.ContainsResource(xy)) - return OpenRa.Game.Order.Harvest(self, xy); + return Order.Harvest(self, xy); return null; } - public Harvester(Actor self) { } + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "Harvest" ) + { + self.CancelActivity(); + self.QueueActivity( new Traits.Activities.Move( order.TargetLocation, 0 ) ); + self.QueueActivity( new Traits.Activities.Harvest() ); + } + else if( order.OrderString == "DeliverOre" ) + { + self.CancelActivity(); + self.QueueActivity( new Traits.Activities.DeliverOre( order.TargetActor ) ); + } + } } } diff --git a/OpenRa.Game/Traits/Helicopter.cs b/OpenRa.Game/Traits/Helicopter.cs index ba14e9a031..dd63b63ed7 100644 --- a/OpenRa.Game/Traits/Helicopter.cs +++ b/OpenRa.Game/Traits/Helicopter.cs @@ -18,16 +18,28 @@ namespace OpenRa.Game.Traits targetLocation = self.Location; } - public Order Order(Actor self, int2 xy, bool lmb, Actor underCursor) + public Order IssueOrder(Actor self, int2 xy, bool lmb, Actor underCursor) { if (lmb) return null; if (underCursor == null) - return OpenRa.Game.Order.Move(self, xy); + return Order.Move(self, xy); return null; } + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "Move" ) + { + targetLocation = order.TargetLocation; + + var attackBase = self.traits.WithInterface().FirstOrDefault(); + if( attackBase != null ) + attackBase.target = null; /* move cancels attack order */ + } + } + public void Tick(Actor self) { var unit = self.traits.Get(); diff --git a/OpenRa.Game/Traits/McvDeploy.cs b/OpenRa.Game/Traits/McvDeploy.cs index da4ca3ecd1..c3052ba33b 100644 --- a/OpenRa.Game/Traits/McvDeploy.cs +++ b/OpenRa.Game/Traits/McvDeploy.cs @@ -10,12 +10,26 @@ namespace OpenRa.Game.Traits { public McvDeploy(Actor self) { } - public Order Order(Actor self, int2 xy, bool lmb, Actor underCursor) + public Order IssueOrder(Actor self, int2 xy, bool lmb, Actor underCursor) { if (lmb) return null; if( xy != self.Location ) return null; - return OpenRa.Game.Order.DeployMcv(self); + return Order.DeployMcv(self); + } + + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "DeployMcv" ) + { + var factBuildingInfo = (UnitInfo.BuildingInfo)Rules.UnitInfo[ "fact" ]; + if( Game.CanPlaceBuilding( factBuildingInfo, self.Location - new int2( 1, 1 ), self, false ) ) + { + self.CancelActivity(); + self.QueueActivity( new Traits.Activities.Turn( 96 ) ); + self.QueueActivity( new Traits.Activities.DeployMcv() ); + } + } } } } diff --git a/OpenRa.Game/Traits/Mobile.cs b/OpenRa.Game/Traits/Mobile.cs index b476d8e848..70e47ca712 100644 --- a/OpenRa.Game/Traits/Mobile.cs +++ b/OpenRa.Game/Traits/Mobile.cs @@ -25,7 +25,7 @@ namespace OpenRa.Game.Traits Game.UnitInfluence.Update( this ); } - public Order Order(Actor self, int2 xy, bool lmb, Actor underCursor) + public Order IssueOrder(Actor self, int2 xy, bool lmb, Actor underCursor) { if( lmb ) return null; @@ -34,7 +34,20 @@ namespace OpenRa.Game.Traits if (xy == toCell) return null; - return OpenRa.Game.Order.Move( self, xy ); + return Order.Move( self, xy ); + } + + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "Move" ) + { + self.CancelActivity(); + self.QueueActivity( new Traits.Activities.Move( order.TargetLocation, 8 ) ); + + var attackBase = self.traits.WithInterface().FirstOrDefault(); + if( attackBase != null ) + attackBase.target = null; /* move cancels attack order */ + } } public IEnumerable OccupiedCells() diff --git a/OpenRa.Game/Traits/RallyPoint.cs b/OpenRa.Game/Traits/RallyPoint.cs index a5dce9541d..e6927cb547 100644 --- a/OpenRa.Game/Traits/RallyPoint.cs +++ b/OpenRa.Game/Traits/RallyPoint.cs @@ -29,10 +29,16 @@ namespace OpenRa.Game.Traits anim.Image, Game.CellSize * (new float2(.5f, .5f) + rallyPoint.ToFloat2())); } - public Order Order(Actor self, int2 xy, bool lmb, Actor underCursor) + public Order IssueOrder(Actor self, int2 xy, bool lmb, Actor underCursor) { if (lmb || underCursor != null) return null; - return OpenRa.Game.Order.SetRallyPoint(self, xy); + return Order.SetRallyPoint(self, xy); + } + + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "SetRallyPoint" ) + rallyPoint = order.TargetLocation; } public void Tick(Actor self) { anim.Tick(); } diff --git a/OpenRa.Game/Traits/TraitsInterfaces.cs b/OpenRa.Game/Traits/TraitsInterfaces.cs index cf3505afce..767fcf6f77 100644 --- a/OpenRa.Game/Traits/TraitsInterfaces.cs +++ b/OpenRa.Game/Traits/TraitsInterfaces.cs @@ -11,8 +11,12 @@ namespace OpenRa.Game.Traits interface ITick { void Tick(Actor self); } interface IRender { IEnumerable> Render(Actor self); } - interface IOrder { Order Order(Actor self, int2 xy, bool lmb, Actor underCursor); } interface INotifyDamage { void Damaged(Actor self, DamageState ds); } interface INotifyDamageEx : INotifyDamage { void Damaged(Actor self, int damage); } interface INotifyBuildComplete { void BuildingComplete (Actor self); } + interface IOrder + { + Order IssueOrder( Actor self, int2 xy, bool lmb, Actor underCursor ); + void ResolveOrder( Actor self, Order order ); + } } diff --git a/OpenRa.Game/UnitOrders.cs b/OpenRa.Game/UnitOrders.cs index 053b5a85c3..e2c3da0273 100755 --- a/OpenRa.Game/UnitOrders.cs +++ b/OpenRa.Game/UnitOrders.cs @@ -14,69 +14,14 @@ namespace OpenRa.Game switch( order.OrderString ) { case "Move": - { - var mobile = order.Subject.traits.GetOrDefault(); - if (mobile != null) - { - order.Subject.CancelActivity( order.Subject ); - order.Subject.QueueActivity( new Traits.Activities.Move( order.TargetLocation, 8 ) ); - } - - var heli = order.Subject.traits.GetOrDefault(); - if (heli != null) - heli.targetLocation = order.TargetLocation; - - var attackBase = order.Subject.traits.WithInterface().FirstOrDefault(); - if( attackBase != null ) - attackBase.target = null; /* move cancels attack order */ - break; - } case "Attack": - { - const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ - var mobile = order.Subject.traits.GetOrDefault(); - /* todo: choose the appropriate weapon, when only one works against this target */ - var weapon = order.Subject.unitInfo.Primary ?? order.Subject.unitInfo.Secondary; - - order.Subject.CancelActivity(order.Subject); - if (order.Subject.traits.Contains()) - { - order.Subject.QueueActivity( - new Traits.Activities.Follow(order.TargetActor, - Math.Max(0, (int)Rules.WeaponInfo[weapon].Range - RangeTolerance))); - - order.Subject.traits.Get().target = order.TargetActor; - } - else - { - order.Subject.QueueActivity( - new Traits.Activities.Attack(order.TargetActor, - Math.Max(0, (int)Rules.WeaponInfo[weapon].Range - RangeTolerance))); - } - break; - } case "DeployMcv": - { - var factBuildingInfo = (UnitInfo.BuildingInfo)Rules.UnitInfo[ "fact" ]; - if( !Game.CanPlaceBuilding( factBuildingInfo, order.Subject.Location - new int2( 1, 1 ), order.Subject, false ) ) - break; /* throw the order on the floor */ - - order.Subject.CancelActivity( order.Subject ); - order.Subject.QueueActivity( new Traits.Activities.Turn( 96 ) ); - order.Subject.QueueActivity( new Traits.Activities.DeployMcv() ); - break; - } case "DeliverOre": - { - order.Subject.CancelActivity( order.Subject ); - order.Subject.QueueActivity( new Traits.Activities.DeliverOre( order.TargetActor ) ); - break; - } case "Harvest": + case "SetRallyPoint": { - order.Subject.CancelActivity( order.Subject ); - order.Subject.QueueActivity( new Traits.Activities.Move( order.TargetLocation, 0 ) ); - order.Subject.QueueActivity( new Traits.Activities.Harvest() ); + foreach( var t in order.Subject.traits.WithInterface() ) + t.ResolveOrder( order.Subject, order ); break; } case "PlaceBuilding": @@ -142,12 +87,6 @@ namespace OpenRa.Game order.Player.CancelProduction( Rules.UnitCategory[ order.TargetString ] ); break; } - case "SetRallyPoint": - { - var pt = order.Subject.traits.Get(); - pt.rallyPoint = order.TargetLocation; - break; - } case "Chat": { Game.chat.AddLine(Pair.New(order.Player.PlayerName + ":", order.TargetString));