diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index f442976916..0feb272ac3 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -364,6 +364,7 @@
+
@@ -405,6 +406,7 @@
+
diff --git a/OpenRA.Mods.Common/Traits/Render/AutoSelectionSize.cs b/OpenRA.Mods.Common/Traits/Render/AutoSelectionSize.cs
new file mode 100644
index 0000000000..ca00a4c077
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Render/AutoSelectionSize.cs
@@ -0,0 +1,31 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation. For more information,
+ * see COPYING.
+ */
+#endregion
+
+using System;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ public class AutoSelectionSizeInfo : ITraitInfo, Requires
+ {
+ public object Create(ActorInitializer init) { return new AutoSelectionSize(this); }
+ }
+
+ public class AutoSelectionSize : IAutoSelectionSize
+ {
+ public AutoSelectionSize(AutoSelectionSizeInfo info) { }
+
+ public int2 SelectionSize(Actor self)
+ {
+ var rs = self.Trait();
+ return rs.AutoSelectionSize(self);
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs
index 06970f8f8f..ac31eaa169 100644
--- a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs
+++ b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs
@@ -236,8 +236,8 @@ namespace OpenRA.Mods.Common.Traits
return sequence;
}
- // Required by RenderSimple
- protected int2 AutoSelectionSize(Actor self)
+ // Required by RenderSimple, WithSpriteBody and WithInfantryBody
+ public int2 AutoSelectionSize(Actor self)
{
return anims.Where(b => b.IsVisible
&& b.Animation.Animation.CurrentSequence != null)
diff --git a/OpenRA.Mods.Common/Traits/Render/WithFacingSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithFacingSpriteBody.cs
new file mode 100644
index 0000000000..81622ae687
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Render/WithFacingSpriteBody.cs
@@ -0,0 +1,46 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation. For more information,
+ * see COPYING.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ public class WithFacingSpriteBodyInfo : WithSpriteBodyInfo, Requires, Requires
+ {
+ public override object Create(ActorInitializer init) { return new WithFacingSpriteBody(init, this); }
+
+ public override IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
+ {
+ var ifacing = init.Actor.Traits.GetOrDefault();
+ var facing = ifacing != null ? init.Contains() ? init.Get() : ifacing.GetInitialFacing() : 0;
+
+ var anim = new Animation(init.World, image, () => facing);
+ anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence));
+
+ yield return new SpriteActorPreview(anim, WVec.Zero, 0, p, rs.Scale);
+ }
+
+ public override int QuantizedBodyFacings(ActorInfo ai, SequenceProvider sequenceProvider, string race)
+ {
+ var rsi = ai.Traits.Get();
+ return sequenceProvider.GetSequence(rsi.GetImage(ai, sequenceProvider, race), Sequence).Facings;
+ }
+ }
+
+ public class WithFacingSpriteBody : WithSpriteBody
+ {
+ public WithFacingSpriteBody(ActorInitializer init, WithFacingSpriteBodyInfo info)
+ : base(init, info, RenderSprites.MakeFacingFunc(init.Self)) { }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs
index 22322a1d22..7e03eb43fb 100644
--- a/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs
+++ b/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits
var rs = self.Trait();
var body = self.Trait();
- anim = new Animation(self.World, rs.GetImage(self), RenderSimple.MakeFacingFunc(self));
+ anim = new Animation(self.World, rs.GetImage(self), RenderSprites.MakeFacingFunc(self));
anim.IsDecoration = true;
anim.Play(info.Sequence);
rs.Add(new AnimationWithOffset(anim,
diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs
index d00cf49ae3..1aa72e50de 100644
--- a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs
+++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs
@@ -9,13 +9,16 @@
#endregion
using System;
+using System.Collections.Generic;
using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Default trait for rendering sprite-based actors.")]
- class WithSpriteBodyInfo : UpgradableTraitInfo, ITraitInfo, Requires
+ public class WithSpriteBodyInfo : UpgradableTraitInfo, ITraitInfo, IRenderActorPreviewSpritesInfo, IQuantizeBodyOrientationInfo,
+ Requires
{
[Desc("Animation to play when the actor is created.")]
public readonly string StartSequence = null;
@@ -23,30 +26,54 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Animation to play when the actor is idle.")]
public readonly string Sequence = "idle";
- public object Create(ActorInitializer init) { return new WithSpriteBody(init, this); }
- }
+ public virtual object Create(ActorInitializer init) { return new WithSpriteBody(init, this); }
- class WithSpriteBody : UpgradableTrait, ISpriteBody
- {
- readonly Animation body;
- readonly WithSpriteBodyInfo info;
-
- public WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info)
- : base(info)
+ public virtual IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{
- this.info = info;
+ var anim = new Animation(init.World, image);
+ anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence));
- var rs = init.Self.Trait();
- body = new Animation(init.Self.World, rs.GetImage(init.Self));
- PlayCustomAnimation(init.Self, info.StartSequence, () => body.PlayRepeating(info.Sequence));
- rs.Add(new AnimationWithOffset(body, null, () => IsTraitDisabled));
+ yield return new SpriteActorPreview(anim, WVec.Zero, 0, p, rs.Scale);
}
- public void PlayCustomAnimation(Actor self, string newAnimation, Action after)
+ public virtual int QuantizedBodyFacings(ActorInfo ai, SequenceProvider sequenceProvider, string race)
{
- body.PlayThen(newAnimation, () =>
+ return 1;
+ }
+ }
+
+ public class WithSpriteBody : UpgradableTrait, ISpriteBody
+ {
+ public readonly Animation DefaultAnimation;
+
+ public WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info)
+ : this(init, info, () => 0) { }
+
+ protected WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info, Func baseFacing)
+ : base(info)
+ {
+ var rs = init.Self.Trait();
+
+ DefaultAnimation = new Animation(init.World, rs.GetImage(init.Self), baseFacing);
+ rs.Add(new AnimationWithOffset(DefaultAnimation, null, () => IsTraitDisabled));
+
+ if (Info.StartSequence != null)
+ PlayCustomAnimation(init.Self, Info.StartSequence,
+ () => DefaultAnimation.PlayRepeating(NormalizeSequence(init.Self, Info.Sequence)));
+ else
+ DefaultAnimation.PlayRepeating(NormalizeSequence(init.Self, Info.Sequence));
+ }
+
+ public string NormalizeSequence(Actor self, string sequence)
+ {
+ return RenderSprites.NormalizeSequence(DefaultAnimation, self.GetDamageState(), sequence);
+ }
+
+ public void PlayCustomAnimation(Actor self, string name, Action after = null)
+ {
+ DefaultAnimation.PlayThen(NormalizeSequence(self, name), () =>
{
- body.Play(info.Sequence);
+ DefaultAnimation.Play(NormalizeSequence(self, Info.Sequence));
if (after != null)
after();
});
@@ -54,14 +81,15 @@ namespace OpenRA.Mods.Common.Traits
public void PlayCustomAnimationRepeating(Actor self, string name)
{
- body.PlayThen(name, () => PlayCustomAnimationRepeating(self, name));
+ DefaultAnimation.PlayThen(name,
+ () => PlayCustomAnimationRepeating(self, name));
}
- public void PlayCustomAnimationBackwards(Actor self, string name, Action after)
+ public void PlayCustomAnimationBackwards(Actor self, string name, Action after = null)
{
- body.PlayBackwardsThen(name, () =>
+ DefaultAnimation.PlayBackwardsThen(NormalizeSequence(self, name), () =>
{
- body.PlayRepeating(info.Sequence);
+ DefaultAnimation.PlayRepeating(NormalizeSequence(self, Info.Sequence));
if (after != null)
after();
});
diff --git a/OpenRA.Mods.Common/Traits/ThrowsParticle.cs b/OpenRA.Mods.Common/Traits/ThrowsParticle.cs
index c9bad3e7a8..3aa7db2d70 100644
--- a/OpenRA.Mods.Common/Traits/ThrowsParticle.cs
+++ b/OpenRA.Mods.Common/Traits/ThrowsParticle.cs
@@ -13,7 +13,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
- class ThrowsParticleInfo : ITraitInfo, Requires, Requires
+ class ThrowsParticleInfo : ITraitInfo, Requires, Requires
{
public readonly string Anim = null;
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
public ThrowsParticle(ActorInitializer init, ThrowsParticleInfo info)
{
var self = init.Self;
- var rs = self.Trait();
+ var rs = self.Trait();
var body = self.Trait();
// TODO: Carry orientation over from the parent instead of just facing
diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
index e6f442919f..59fb134a0e 100644
--- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
@@ -1016,6 +1016,37 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
+ if (engineVersion < 20150528)
+ {
+ // Note (stolen from WithInfantryBody upgrade rule):
+ // These rules are set up to do approximately the right thing for maps, but
+ // mods need additional manual tweaks. This is the best we can do without having
+ // much smarter rules parsing, because we currently can't reason about inherited traits.
+ if (depth == 0)
+ {
+ var childKeys = new[] { "Sequence" };
+
+ var ru = node.Value.Nodes.FirstOrDefault(n => n.Key == "RenderUnit");
+ if (ru != null)
+ {
+ ru.Key = "WithFacingSpriteBody";
+ node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));
+
+ var rsNodes = ru.Value.Nodes.Where(n => !childKeys.Contains(n.Key)).ToList();
+ if (rsNodes.Any())
+ node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));
+ else
+ node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", ""));
+
+ ru.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
+ }
+
+ var rru = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderUnit");
+ if (rru != null)
+ rru.Key = "-WithFacingSpriteBody";
+ }
+ }
+
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}