diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index f002d16e56..779f4b61c8 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -220,6 +220,7 @@ + diff --git a/OpenRA.Mods.RA/Activities/Drag.cs b/OpenRA.Game/Traits/Activities/Drag.cs similarity index 95% rename from OpenRA.Mods.RA/Activities/Drag.cs rename to OpenRA.Game/Traits/Activities/Drag.cs index 0b6096a458..46080ad81d 100644 --- a/OpenRA.Mods.RA/Activities/Drag.cs +++ b/OpenRA.Game/Traits/Activities/Drag.cs @@ -10,7 +10,7 @@ using OpenRA.Traits; -namespace OpenRA.Mods.RA.Activities +namespace OpenRA.Traits.Activities { public class Drag : IActivity { diff --git a/OpenRA.Game/Traits/Production.cs b/OpenRA.Game/Traits/Production.cs index 2f11c8f686..c21ead443f 100755 --- a/OpenRA.Game/Traits/Production.cs +++ b/OpenRA.Game/Traits/Production.cs @@ -16,86 +16,98 @@ using OpenRA.FileFormats; namespace OpenRA.Traits { - public class ProductionInfo : TraitInfo + public class ProductionInfo : ITraitInfo { - public readonly int[] SpawnOffset = null; - public readonly int[] ProductionOffset = null; - public readonly int[] ExitOffset = null; + public readonly float[] SpawnOffsets; // in px relative to CenterLocation + public readonly int[] ExitCells; // in cells relative to TopLeft, supports a list for multiple exits public readonly bool EnablePrimary = true; public readonly string[] Produces = { }; + + public virtual object Create(ActorInitializer init) { return new Production(this); } } public class Production : IIssueOrder, IResolveOrder, ITags, IOrderCursor { - public virtual int2? CreationLocation( Actor self, ActorInfo producee ) + public readonly Dictionary Spawns = new Dictionary(); + public Production(ProductionInfo info) { - var pos = Util.CellContaining(self.CenterLocation); - var pi = self.Info.Traits.Get(); - if (pi.ProductionOffset != null) - pos += pi.ProductionOffset.AsInt2(); - return pos; - } - - public virtual int2? ExitLocation(Actor self, ActorInfo producee) - { - var pos = Util.CellContaining(self.CenterLocation); - var pi = self.Info.Traits.Get(); - if (pi.ExitOffset != null) - pos += pi.ExitOffset.AsInt2(); - return pos; + if (info.SpawnOffsets == null || info.ExitCells == null) + return; + + if (info.SpawnOffsets.Length != info.ExitCells.Length) + throw new System.InvalidOperationException("SpawnOffset, ExitCells length mismatch"); + + for (int i = 0; i < info.ExitCells.Length; i+=2) + Spawns.Add(new float2(info.SpawnOffsets[i],info.SpawnOffsets[i+1]), new int2(info.ExitCells[i], info.ExitCells[i+1])); } - public virtual int CreationFacing( Actor self, Actor newUnit ) - { - return newUnit.traits.Get().InitialFacing; - } - public virtual bool Produce( Actor self, ActorInfo producee ) - { - var location = CreationLocation( self, producee ); - if( location == null || self.World.WorldActor.traits.Get().GetUnitsAt( location.Value ).Any() ) - return false; - - var newUnit = self.World.CreateActor( producee.Name, new TypeDictionary + { + var newUnit = self.World.CreateActor(false, producee.Name, new TypeDictionary { - new LocationInit( location.Value ), new OwnerInit( self.Owner ), }); - - var pi = self.Info.Traits.Get(); - var rp = self.traits.GetOrDefault(); - if (rp != null || pi.ExitOffset != null) + + // Todo: remove assumption on Mobile + var mobile = newUnit.traits.Get(); + + // Pick an exit that we can move to + var exit = int2.Zero; + var spawn = float2.Zero; + var success = false; + + // Pick a spawn/exit point + // Todo: Reorder in a synced random way + foreach (var s in Spawns) { - var mobile = newUnit.traits.GetOrDefault(); - if (mobile != null) + exit = self.Location + s.Value; + spawn = self.CenterLocation + s.Key; + if (mobile.CanEnterCell(exit,self,true)) { - int2? target = null; - if (pi.ExitOffset != null) - { - target = ExitLocation(self, producee).Value; - newUnit.QueueActivity(new Activities.Move(target.Value, 1)); - } - - if (rp != null) - { - target = rp.rallyPoint; - newUnit.QueueActivity(new Activities.Move(target.Value, 1)); - } - - if (target != null && newUnit.Owner == self.World.LocalPlayer) - { - self.World.AddFrameEndTask(w => - { - var line = newUnit.traits.GetOrDefault(); - if (line != null) - line.SetTargetSilently(newUnit, Target.FromCell(target.Value), Color.Green); - }); - } + success = true; + break; } } + + if (!success) + { + // Hack around mobile being a tard; remove from UIM (we shouldn't be there in the first place) + newUnit.traits.Get().RemoveInfluence(); + return false; + } + + // Unit can be built; add to the world + self.World.Add(newUnit); + + // Set the physical position of the unit as the exit cell + mobile.SetPosition(newUnit,exit); + var to = Util.CenterOfCell(exit); - if (pi != null && pi.SpawnOffset != null) - newUnit.CenterLocation = self.CenterLocation + pi.SpawnOffset.AsInt2(); + // Animate the spawn -> exit transition + newUnit.CenterLocation = spawn; + mobile.Facing = Util.GetFacing(to - spawn, mobile.Facing); + var speed = mobile.MovementSpeedForCell(self, exit); + var length = speed > 0 ? (int)( ( to - spawn ).Length*3 / speed ) : 0; + newUnit.QueueActivity(new Activities.Drag(spawn, to, length)); + + // For the target line + var target = exit; + var rp = self.traits.GetOrDefault(); + if (rp != null) + { + target = rp.rallyPoint; + newUnit.QueueActivity(new Activities.Move(target, 1)); + } + + if (newUnit.Owner == self.World.LocalPlayer) + { + self.World.AddFrameEndTask(w => + { + var line = newUnit.traits.GetOrDefault(); + if (line != null) + line.SetTargetSilently(newUnit, Target.FromCell(target), Color.Green); + }); + } foreach (var t in self.traits.WithInterface()) t.UnitProduced(self, newUnit); diff --git a/OpenRA.Mods.Cnc/ProductionAirdrop.cs b/OpenRA.Mods.Cnc/ProductionAirdrop.cs index aedf1ceb6f..f22f93acac 100644 --- a/OpenRA.Mods.Cnc/ProductionAirdrop.cs +++ b/OpenRA.Mods.Cnc/ProductionAirdrop.cs @@ -21,11 +21,13 @@ namespace OpenRA.Mods.Cnc { public class ProductionAirdropInfo : ProductionInfo { - public override object Create(ActorInitializer init) { return new ProductionAirdrop(); } + public override object Create(ActorInitializer init) { return new ProductionAirdrop(this); } } class ProductionAirdrop : Production { + public ProductionAirdrop(ProductionAirdropInfo info) : base(info) {} + public override bool Produce( Actor self, ActorInfo producee ) { var owner = self.Owner; diff --git a/OpenRA.Mods.RA/Activities/HeliReturn.cs b/OpenRA.Mods.RA/Activities/HeliReturn.cs index 28676a46b4..c60cf4b5ed 100644 --- a/OpenRA.Mods.RA/Activities/HeliReturn.cs +++ b/OpenRA.Mods.RA/Activities/HeliReturn.cs @@ -44,12 +44,11 @@ namespace OpenRA.Mods.RA.Activities if (res != null) self.traits.Get().reservation = res.Reserve(self); - var pi = dest.Info.Traits.GetOrDefault(); - var offset = pi != null ? pi.SpawnOffset : null; - var offsetVec = offset != null ? new float2(offset[0], offset[1]) : float2.Zero; + var pi = dest.traits.Get(); + var offset = pi != null ? pi.Spawns.First().Key : float2.Zero; return Util.SequenceActivities( - new HeliFly(dest.CenterLocation + offsetVec), + new HeliFly(dest.CenterLocation + offset), new Turn(initialFacing), new HeliLand(false), new Rearm(), diff --git a/OpenRA.Mods.RA/Helicopter.cs b/OpenRA.Mods.RA/Helicopter.cs index 0695e1b4d7..8bf81537df 100644 --- a/OpenRA.Mods.RA/Helicopter.cs +++ b/OpenRA.Mods.RA/Helicopter.cs @@ -104,9 +104,8 @@ namespace OpenRA.Mods.RA if (res != null) reservation = res.Reserve(self); - var productionInfo = order.TargetActor.Info.Traits.GetOrDefault(); - var offset = productionInfo != null ? productionInfo.SpawnOffset : null; - var offsetVec = offset != null ? new float2(offset[0], offset[1]) : float2.Zero; + var pi = order.TargetActor.traits.Get(); + var offset = pi != null ? pi.Spawns.First().Key : float2.Zero; if (self.Owner == self.World.LocalPlayer) self.World.AddFrameEndTask(w => @@ -118,7 +117,7 @@ namespace OpenRA.Mods.RA }); self.CancelActivity(); - self.QueueActivity(new HeliFly(order.TargetActor.CenterLocation + offsetVec)); + self.QueueActivity(new HeliFly(order.TargetActor.CenterLocation + offset)); self.QueueActivity(new Turn(Info.InitialFacing)); self.QueueActivity(new HeliLand(false)); self.QueueActivity(Info.RearmBuildings.Contains(order.TargetActor.Info.Name) diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index b312571a8b..46fee83fe5 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -1,4 +1,4 @@ - + Debug @@ -215,7 +215,6 @@ - diff --git a/OpenRA.Mods.RA/ProducesHelicopters.cs b/OpenRA.Mods.RA/ProducesHelicopters.cs index b19c9e1ed5..5bf8f5044e 100644 --- a/OpenRA.Mods.RA/ProducesHelicopters.cs +++ b/OpenRA.Mods.RA/ProducesHelicopters.cs @@ -17,25 +17,29 @@ namespace OpenRA.Mods.RA { public class ProducesHelicoptersInfo : ProductionInfo { - public override object Create(ActorInitializer init) { return new ProducesHelicopters(); } + public override object Create(ActorInitializer init) { return new ProducesHelicopters(this); } } class ProducesHelicopters : Production { + public ProducesHelicopters(ProducesHelicoptersInfo info) : base(info) {} + + /* // Hack around visibility bullshit in Production public override bool Produce( Actor self, ActorInfo producee ) { var location = CreationLocation( self, producee ); if( location == null || self.World.WorldActor.traits.Get().GetUnitsAt( location.Value ).Any() ) return false; - + + var pi = self.Info.Traits.Get(); var newUnit = self.World.CreateActor( producee.Name, new TypeDictionary { new LocationInit( location.Value ), new OwnerInit( self.Owner ), + new FacingInit( pi.ProductionFacing ), }); - var pi = self.Info.Traits.Get(); var rp = self.traits.GetOrDefault(); if( rp != null || pi.ExitOffset != null) { @@ -59,6 +63,7 @@ namespace OpenRA.Mods.RA Log.Write("debug", "{0} #{1} produced by {2} #{3}", newUnit.Info.Name, newUnit.ActorID, self.Info.Name, self.ActorID); return true; - } + } + */ } } diff --git a/OpenRA.Mods.RA/ProductionSurround.cs b/OpenRA.Mods.RA/ProductionSurround.cs index ad70dff157..cec3ec40f0 100644 --- a/OpenRA.Mods.RA/ProductionSurround.cs +++ b/OpenRA.Mods.RA/ProductionSurround.cs @@ -16,11 +16,13 @@ namespace OpenRA.Mods.RA { class ProductionSurroundInfo : ProductionInfo { - public override object Create(ActorInitializer init) { return new ProductionSurround(); } + public override object Create(ActorInitializer init) { return new ProductionSurround(this); } } class ProductionSurround : Production { + public ProductionSurround(ProductionSurroundInfo info) : base(info) {} + static int2? FindAdjacentTile(Actor self, bool waterBound) { var tiles = Footprint.Tiles(self); @@ -34,7 +36,7 @@ namespace OpenRA.Mods.RA return null; } - + /* public override int2? CreationLocation(Actor self, ActorInfo producee) { return FindAdjacentTile(self, self.Info.Traits.Get().WaterBound); @@ -44,5 +46,6 @@ namespace OpenRA.Mods.RA { return Util.GetFacing(newUnit.CenterLocation - self.CenterLocation, 128); } + */ } } diff --git a/OpenRA.Mods.RA/Render/RenderInfantry.cs b/OpenRA.Mods.RA/Render/RenderInfantry.cs index 132eabcd75..cae8cdf86c 100644 --- a/OpenRA.Mods.RA/Render/RenderInfantry.cs +++ b/OpenRA.Mods.RA/Render/RenderInfantry.cs @@ -29,7 +29,7 @@ namespace OpenRA.Mods.RA.Render bool ChooseMoveAnim(Actor self) { - if (!(self.GetCurrentActivity() is Move)) + if (!(self.GetCurrentActivity() is Move) && !(self.GetCurrentActivity() is Drag)) // A bit of a hack return false; var mobile = self.traits.Get(); diff --git a/OpenRA.Mods.RA/ReservableProduction.cs b/OpenRA.Mods.RA/ReservableProduction.cs index 961409f3da..6a7aaf26b8 100644 --- a/OpenRA.Mods.RA/ReservableProduction.cs +++ b/OpenRA.Mods.RA/ReservableProduction.cs @@ -17,11 +17,13 @@ namespace OpenRA.Mods.RA public class ReservableProductionInfo : ProductionInfo, ITraitPrerequisite { - public override object Create(ActorInitializer init) { return new ReservableProduction(); } + public override object Create(ActorInitializer init) { return new ReservableProduction(this); } } class ReservableProduction : Production { + public ReservableProduction(ReservableProductionInfo info) : base(info) {} + public override bool Produce(Actor self, ActorInfo producee) { if (Reservable.IsReserved(self)) diff --git a/mods/cnc/structures.yaml b/mods/cnc/structures.yaml index 624454fbec..8dbc1b4aa3 100644 --- a/mods/cnc/structures.yaml +++ b/mods/cnc/structures.yaml @@ -158,6 +158,8 @@ PYLE: RallyPoint: Production: Produces: Infantry + SpawnOffsets: -10,2, 7,7 + ExitCells: 0,1, 1,1 HAND: Inherits: ^Building @@ -184,6 +186,8 @@ HAND: RallyPoint: Production: Produces: Infantry + SpawnOffsets: 12,24 + ExitCells:1,2 AFLD: Inherits: ^Building @@ -237,7 +241,7 @@ WEAP: RenderWarFactory: RallyPoint: Production: - ExitOffset:-1,1 +# ExitOffset:-1,1 Produces: Vehicle HQ: RequiresPower: @@ -335,7 +339,7 @@ HPAD: Range: 5 Bib: ProducesHelicopters: - SpawnOffset: 0,-4 + SpawnOffsets: 0,-4 Produces: Plane BelowUnits: Reservable: