diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj
index 3e77f366f9..fae4b8e99e 100644
--- a/OpenRa.Game/OpenRa.Game.csproj
+++ b/OpenRa.Game/OpenRa.Game.csproj
@@ -130,7 +130,7 @@
-
+
@@ -236,7 +236,7 @@
-
+
diff --git a/OpenRa.Game/Orders/UnitOrderGenerator.cs b/OpenRa.Game/Orders/UnitOrderGenerator.cs
index c58b19617a..fae7bcba92 100644
--- a/OpenRa.Game/Orders/UnitOrderGenerator.cs
+++ b/OpenRa.Game/Orders/UnitOrderGenerator.cs
@@ -67,12 +67,17 @@ namespace OpenRa.Orders
return Cursor.Move;
else
return Cursor.MoveBlocked;
- case "DeployMcv":
- var factBuildingInfo = Rules.Info["fact"].Traits.Get();
- if (a.World.CanPlaceBuilding("fact", factBuildingInfo, a.Location - new int2(1, 1), a))
- return Cursor.Deploy;
- else
- return Cursor.DeployBlocked;
+ case "DeployTransform":
+ var depInfo = a.Info.Traits.Get();
+ var transInfo = Rules.Info[depInfo.TransformsInto];
+ if (transInfo.Traits.Contains())
+ {
+ var bi = transInfo.Traits.Get();
+ if (!a.World.CanPlaceBuilding(depInfo.TransformsInto, bi, a.Location + new int2(depInfo.Offset[0], depInfo.Offset[1]), a))
+ return Cursor.DeployBlocked;
+ }
+ return Cursor.Deploy;
+
case "Deploy": return Cursor.Deploy;
case "Enter": return Cursor.Enter;
case "EnterTransport": return Cursor.Enter;
diff --git a/OpenRa.Game/Traits/Activities/DeployMcv.cs b/OpenRa.Game/Traits/Activities/DeployMcv.cs
deleted file mode 100755
index cd8d0f3e1f..0000000000
--- a/OpenRa.Game/Traits/Activities/DeployMcv.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-
-namespace OpenRa.Traits.Activities
-{
- class DeployMcv : IActivity
- {
- public IActivity NextActivity { get; set; }
-
- public IActivity Tick( Actor self )
- {
- self.World.AddFrameEndTask( _ =>
- {
- self.Health = 0;
- self.World.Remove( self );
- Sound.PlayToPlayer(self.Owner, "placbldg.aud");
- Sound.PlayToPlayer(self.Owner, "build5.aud");
- self.World.CreateActor( "fact", self.Location - new int2( 1, 1 ), self.Owner );
- } );
- return this;
- }
-
- public void Cancel( Actor self )
- {
- // Cancel can't happen between this being moved to the head of the list, and it being Ticked.
- throw new InvalidOperationException( "DeployMcvAction: Cancel() should never occur." );
- }
- }
-}
diff --git a/OpenRa.Game/Traits/Activities/TransformIntoActor.cs b/OpenRa.Game/Traits/Activities/TransformIntoActor.cs
new file mode 100644
index 0000000000..eaf5e18209
--- /dev/null
+++ b/OpenRa.Game/Traits/Activities/TransformIntoActor.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace OpenRa.Traits.Activities
+{
+ class TransformIntoActor : IActivity
+ {
+ string actor = null;
+ int2 offset;
+ string[] sounds = null;
+ bool transferPercentage;
+
+ bool isCanceled;
+
+ public TransformIntoActor(string actor, int2 offset, bool transferHealthPercentage, string[] sounds)
+ {
+ this.actor = actor;
+ this.offset = offset;
+ this.sounds = sounds;
+ this.transferPercentage = transferHealthPercentage;
+ }
+
+ public IActivity NextActivity { get; set; }
+
+ public IActivity Tick( Actor self )
+ {
+ if (isCanceled) return NextActivity;
+
+ self.World.AddFrameEndTask( _ =>
+ {
+ var oldHP = self.GetMaxHP();
+ var newHP = Rules.Info[actor].Traits.Get().HP;
+ var newHealth = (transferPercentage) ? (int)((float)self.Health/oldHP*newHP) : Math.Min(self.Health, newHP);
+
+ self.Health = 0;
+ self.World.Remove( self );
+ foreach (var s in sounds)
+ Sound.PlayToPlayer(self.Owner, s);
+
+ var a = self.World.CreateActor( actor, self.Location + offset, self.Owner );
+ a.Health = newHealth;
+ } );
+ return this;
+ }
+
+ public void Cancel(Actor self) { isCanceled = true; NextActivity = null; }
+ }
+}
diff --git a/OpenRa.Game/Traits/McvDeploy.cs b/OpenRa.Game/Traits/McvDeploy.cs
deleted file mode 100644
index 5e72aaeeec..0000000000
--- a/OpenRa.Game/Traits/McvDeploy.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using OpenRa.GameRules;
-using OpenRa.Traits.Activities;
-
-namespace OpenRa.Traits
-{
- class McvDeployInfo : ITraitInfo
- {
- public object Create(Actor self) { return new McvDeploy(self); }
- }
-
- class McvDeploy : IIssueOrder, IResolveOrder
- {
- public McvDeploy(Actor self) { }
-
- public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
- {
- if (mi.Button == MouseButton.Right && self == underCursor)
- return new Order("DeployMcv", self);
-
- return null;
- }
-
- public void ResolveOrder( Actor self, Order order )
- {
- if( order.OrderString == "DeployMcv" )
- {
- var factBuildingInfo = Rules.Info[ "fact" ].Traits.Get();
- if( self.World.CanPlaceBuilding( "fact", factBuildingInfo, self.Location - new int2( 1, 1 ), self ) )
- {
- self.CancelActivity();
- self.QueueActivity( new Turn( 96 ) );
- self.QueueActivity( new DeployMcv() );
- }
- }
- }
- }
-}
diff --git a/OpenRa.Game/Traits/PlaceBuilding.cs b/OpenRa.Game/Traits/PlaceBuilding.cs
index de1db29ed3..0f6adcaabb 100755
--- a/OpenRa.Game/Traits/PlaceBuilding.cs
+++ b/OpenRa.Game/Traits/PlaceBuilding.cs
@@ -26,6 +26,8 @@ namespace OpenRa.Traits
Sound.PlayToPlayer(order.Player, "placbldg.aud");
Sound.PlayToPlayer(order.Player, "build5.aud");
+
+ // TODO: Prioritise the primary conyard if it exists
var fact = self.World.Queries
.OwnedBy[self.Owner]
.WithTrait()
diff --git a/OpenRa.Game/Traits/TransformsOnDeploy.cs b/OpenRa.Game/Traits/TransformsOnDeploy.cs
new file mode 100644
index 0000000000..5ce6a5a515
--- /dev/null
+++ b/OpenRa.Game/Traits/TransformsOnDeploy.cs
@@ -0,0 +1,68 @@
+using OpenRa.GameRules;
+using OpenRa.Traits.Activities;
+using System;
+
+namespace OpenRa.Traits
+{
+ class TransformsOnDeployInfo : ITraitInfo
+ {
+ public readonly string TransformsInto = null;
+ public readonly int[] Offset = null;
+ public readonly int[] DeployDirections = new int[] {96};
+ public readonly bool TransferHealthPercentage = true; // Set to false to transfer the absolute health
+ public readonly string[] TransformSounds = null;
+ public readonly string[] NoTransformSounds = null;
+
+ public object Create(Actor self) { return new TransformsOnDeploy(self); }
+ }
+
+ class TransformsOnDeploy : IIssueOrder, IResolveOrder
+ {
+ public TransformsOnDeploy(Actor self) { }
+
+ 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 void ResolveOrder( Actor self, Order order )
+ {
+ if (order.OrderString == "DeployTransform")
+ {
+ var info = self.Info.Traits.Get();
+
+ var transInfo = Rules.Info[info.TransformsInto];
+
+ if (transInfo.Traits.Contains())
+ {
+ var bi = transInfo.Traits.Get();
+ if (!self.World.CanPlaceBuilding(info.TransformsInto, bi, self.Location + new int2(info.Offset[0], info.Offset[1]), self))
+ {
+ foreach (var s in info.NoTransformSounds)
+ Sound.PlayToPlayer(self.Owner, s);
+
+ return;
+ }
+
+ }
+ self.CancelActivity();
+
+ // Pick the closed deploy direction to turn to
+ if (self.traits.Contains())
+ {
+ var unit = self.traits.Get();
+
+ // TODO: Pick the closest deploy direction
+ var bestDir = info.DeployDirections[0];
+
+ self.QueueActivity(new Turn(bestDir));
+ }
+
+ self.QueueActivity(new TransformIntoActor(info.TransformsInto, new int2(info.Offset[0], info.Offset[1]), info.TransferHealthPercentage, info.TransformSounds));
+ }
+ }
+ }
+}
diff --git a/mods/cnc/vehicles.yaml b/mods/cnc/vehicles.yaml
index d1820af32d..1ee0e45db0 100644
--- a/mods/cnc/vehicles.yaml
+++ b/mods/cnc/vehicles.yaml
@@ -16,7 +16,12 @@ MCV:
Crewed: yes
Sight: 2
Speed: 12
- McvDeploy:
+ TransformsOnDeploy:
+ TransformsInto: fact
+ Offset:-1,-1
+ DeployDirections: 96
+ TransformSounds: placbldg.aud, build5.aud
+ NoTransformSounds: nodeply1.aud
RenderUnit:
HARV:
diff --git a/mods/ra/rules.yaml b/mods/ra/rules.yaml
index e4a62f16b8..11f089cb56 100644
--- a/mods/ra/rules.yaml
+++ b/mods/ra/rules.yaml
@@ -734,7 +734,12 @@ MCV:
Crewed: yes
Sight: 4
Speed: 6
- McvDeploy:
+ TransformsOnDeploy:
+ TransformsInto: fact
+ Offset:-1,-1
+ DeployDirections: 96
+ TransformSounds: placbldg.aud, build5.aud
+ NoTransformSounds: nodeply1.aud
RenderUnit:
JEEP: