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: