diff --git a/OpenRA.Mods.Common/Activities/SpriteHarvesterDockSequence.cs b/OpenRA.Mods.Common/Activities/SpriteHarvesterDockSequence.cs index a5b78bdc92..89da5f73bf 100644 --- a/OpenRA.Mods.Common/Activities/SpriteHarvesterDockSequence.cs +++ b/OpenRA.Mods.Common/Activities/SpriteHarvesterDockSequence.cs @@ -15,25 +15,37 @@ namespace OpenRA.Mods.Common.Activities { public class SpriteHarvesterDockSequence : HarvesterDockSequence { - readonly RenderUnit ru; + readonly WithSpriteBody wsb; + readonly WithDockingAnimation wda; public SpriteHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) : base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength) { - ru = self.Trait(); + wsb = self.Trait(); + wda = self.Trait(); } public override Activity OnStateDock(Actor self) { - ru.PlayCustomAnimation(self, "dock", () => ru.PlayCustomAnimationRepeating(self, "dock-loop")); + foreach (var trait in self.TraitsImplementing()) + trait.Docked(); + + wsb.PlayCustomAnimation(self, wda.Info.DockSequence, () => wsb.PlayCustomAnimationRepeating(self, wda.Info.DockLoopSequence)); dockingState = State.Loop; return this; } public override Activity OnStateUndock(Actor self) { - ru.PlayCustomAnimationBackwards(self, "dock", () => dockingState = State.Complete); + wsb.PlayCustomAnimationBackwards(self, wda.Info.DockSequence, + () => + { + dockingState = State.Complete; + foreach (var trait in self.TraitsImplementing()) + trait.Undocked(); + }); dockingState = State.Wait; + return this; } } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index f08f6c0617..79cbc8933e 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -397,11 +397,9 @@ - - @@ -421,8 +419,10 @@ + + diff --git a/OpenRA.Mods.Common/Traits/Render/RenderHarvester.cs b/OpenRA.Mods.Common/Traits/Render/RenderHarvester.cs deleted file mode 100644 index a3f8ca054c..0000000000 --- a/OpenRA.Mods.Common/Traits/Render/RenderHarvester.cs +++ /dev/null @@ -1,63 +0,0 @@ -#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 OpenRA.Activities; -using OpenRA.Graphics; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Traits -{ - class RenderHarvesterInfo : RenderUnitInfo, Requires - { - public readonly string[] ImagesByFullness = { "harv" }; - - [SequenceReference("ImagesByFullness")] public readonly string HarvestSequence = "harvest"; - - public override object Create(ActorInitializer init) { return new RenderHarvester(init, this); } - } - - class RenderHarvester : RenderUnit, INotifyHarvesterAction - { - Harvester harv; - RenderHarvesterInfo info; - - public RenderHarvester(ActorInitializer init, RenderHarvesterInfo info) - : base(init, info) - { - this.info = info; - harv = init.Self.Trait(); - - // HACK: Force images to be loaded up-front - foreach (var image in info.ImagesByFullness) - new Animation(init.World, image); - } - - public override void Tick(Actor self) - { - var desiredState = harv.Fullness * (info.ImagesByFullness.Length - 1) / 100; - var desiredImage = info.ImagesByFullness[desiredState]; - - if (DefaultAnimation.Name != desiredImage) - DefaultAnimation.ChangeImage(desiredImage, info.Sequence); - - base.Tick(self); - } - - public void Harvested(Actor self, ResourceType resource) - { - if (DefaultAnimation.CurrentSequence.Name != info.HarvestSequence) - PlayCustomAnim(self, info.HarvestSequence); - } - - public void MovingToResources(Actor self, CPos targetCell, Activity next) { } - public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { } - public void MovementCancelled(Actor self) { } - } -} diff --git a/OpenRA.Mods.Common/Traits/Render/RenderUnit.cs b/OpenRA.Mods.Common/Traits/Render/RenderUnit.cs deleted file mode 100644 index 832544bf63..0000000000 --- a/OpenRA.Mods.Common/Traits/Render/RenderUnit.cs +++ /dev/null @@ -1,50 +0,0 @@ -#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 -{ - [Desc("Render trait for non-animated actors that have sprites facing into each direction.", - "Deprecated. This will soon be removed, use RenderSprites + WithFacingSpriteBody instead.")] - public class RenderUnitInfo : RenderSimpleInfo, Requires - { - public override object Create(ActorInitializer init) { return new RenderUnit(init, this); } - } - - public class RenderUnit : RenderSimple, ISpriteBody - { - readonly RenderUnitInfo info; - - public RenderUnit(ActorInitializer init, RenderUnitInfo info) - : base(init, info) - { - this.info = info; - } - - public void PlayCustomAnimation(Actor self, string newAnimation, Action after) - { - DefaultAnimation.PlayThen(newAnimation, () => { DefaultAnimation.Play(info.Sequence); if (after != null) after(); }); - } - - public void PlayCustomAnimationRepeating(Actor self, string name) - { - DefaultAnimation.PlayThen(name, - () => PlayCustomAnimationRepeating(self, name)); - } - - public void PlayCustomAnimationBackwards(Actor self, string name, Action after) - { - DefaultAnimation.PlayBackwardsThen(name, - () => { DefaultAnimation.PlayRepeating(info.Sequence); if (after != null) after(); }); - } - } -} diff --git a/OpenRA.Mods.Common/Traits/Render/WithAttackAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithAttackAnimation.cs index 5e65c5d15c..bb9628acdb 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithAttackAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithAttackAnimation.cs @@ -9,10 +9,9 @@ #endregion using System.Linq; -using OpenRA.Mods.Common.Traits; using OpenRA.Traits; -namespace OpenRA.Mods.RA.Traits +namespace OpenRA.Mods.Common.Traits { public class WithAttackAnimationInfo : ITraitInfo, Requires, Requires, Requires { diff --git a/OpenRA.Mods.Common/Traits/Render/WithDockingAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithDockingAnimation.cs new file mode 100644 index 0000000000..252b4a53d2 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithDockingAnimation.cs @@ -0,0 +1,35 @@ +#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 OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + public class WithDockingAnimationInfo : ITraitInfo, Requires, Requires + { + [Desc("Displayed when docking to refinery.")] + [SequenceReference] public readonly string DockSequence = "dock"; + + [Desc("Looped while unloading at refinery.")] + [SequenceReference] public readonly string DockLoopSequence = "dock-loop"; + + public object Create(ActorInitializer init) { return new WithDockingAnimation(init, this); } + } + + public class WithDockingAnimation + { + public readonly WithDockingAnimationInfo Info; + + public WithDockingAnimation(ActorInitializer init, WithDockingAnimationInfo info) + { + Info = info; + } + } +} diff --git a/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs index b8c494ed74..fdc0bc196d 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithHarvestAnimation.cs @@ -9,64 +9,79 @@ #endregion using OpenRA.Activities; -using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - [Desc("Displays an overlay whenever resources are harvested by the actor.")] - class WithHarvestAnimationInfo : ITraitInfo, Requires, Requires + public class WithHarvestAnimationInfo : ITraitInfo, Requires, Requires { - [Desc("Sequence name to use")] - [SequenceReference] public readonly string Sequence = "harvest"; + [Desc("Prefix added to idle and harvest sequences depending on fullness of harvester.")] + [SequenceReference(null, true)] public readonly string[] PrefixByFullness = { "" }; - [Desc("Position relative to body")] - public readonly WVec Offset = WVec.Zero; + [Desc("Displayed while harvesting.")] + [SequenceReference] public readonly string HarvestSequence = "harvest"; - public readonly string Palette = "effect"; - - public object Create(ActorInitializer init) { return new WithHarvestAnimation(init.Self, this); } + public object Create(ActorInitializer init) { return new WithHarvestAnimation(init, this); } } - class WithHarvestAnimation : INotifyHarvesterAction + public class WithHarvestAnimation : ITick, INotifyHarvesterAction { - WithHarvestAnimationInfo info; - Animation anim; - bool visible; + public readonly WithHarvestAnimationInfo Info; + readonly WithSpriteBody wsb; + readonly Harvester harv; - public WithHarvestAnimation(Actor self, WithHarvestAnimationInfo info) + public bool IsModifying; + + public WithHarvestAnimation(ActorInitializer init, WithHarvestAnimationInfo info) { - this.info = info; - var rs = self.Trait(); - var body = self.Trait(); + Info = info; + harv = init.Self.Trait(); + wsb = init.Self.Trait(); + } - anim = new Animation(self.World, rs.GetImage(self), RenderSprites.MakeFacingFunc(self)); - anim.IsDecoration = true; - anim.Play(info.Sequence); - rs.Add(new AnimationWithOffset(anim, - () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), - () => !visible, - () => false, - p => ZOffsetFromCenter(self, p, 0)), info.Palette); + protected virtual string NormalizeHarvesterSequence(Actor self, string baseSequence) + { + var desiredState = harv.Fullness * (Info.PrefixByFullness.Length - 1) / 100; + var desiredPrefix = Info.PrefixByFullness[desiredState]; + + if (wsb.DefaultAnimation.HasSequence(desiredPrefix + baseSequence)) + return desiredPrefix + baseSequence; + else + return baseSequence; + } + + public void Tick(Actor self) + { + if (!IsModifying && !string.IsNullOrEmpty(wsb.Info.Sequence) && wsb.DefaultAnimation.HasSequence(NormalizeHarvesterSequence(self, wsb.Info.Sequence))) + { + if (wsb.DefaultAnimation.CurrentSequence.Name != NormalizeHarvesterSequence(self, wsb.Info.Sequence)) + wsb.DefaultAnimation.ReplaceAnim(NormalizeHarvesterSequence(self, wsb.Info.Sequence)); + } } public void Harvested(Actor self, ResourceType resource) { - if (visible) - return; + if (!IsModifying && !string.IsNullOrEmpty(Info.HarvestSequence) && wsb.DefaultAnimation.HasSequence(NormalizeHarvesterSequence(self, Info.HarvestSequence))) + { + IsModifying = true; + wsb.PlayCustomAnimation(self, NormalizeHarvesterSequence(self, Info.HarvestSequence), () => IsModifying = false); + } + } - visible = true; - anim.PlayThen(info.Sequence, () => visible = false); + // If IsModifying isn't set to true, the docking animation + // will be overridden by the WithHarvestAnimation fullness modifier. + public void Docked() + { + IsModifying = true; + } + + public void Undocked() + { + IsModifying = false; } public void MovingToResources(Actor self, CPos targetCell, Activity next) { } public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { } public void MovementCancelled(Actor self) { } - - public static int ZOffsetFromCenter(Actor self, WPos pos, int offset) - { - var delta = self.CenterPosition - pos; - return delta.Y + delta.Z + offset; - } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithHarvestOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithHarvestOverlay.cs new file mode 100644 index 0000000000..ce6ed88fa1 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithHarvestOverlay.cs @@ -0,0 +1,74 @@ +#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 OpenRA.Activities; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Displays an overlay whenever resources are harvested by the actor.")] + class WithHarvestOverlayInfo : ITraitInfo, Requires, Requires + { + [Desc("Sequence name to use")] + [SequenceReference] public readonly string Sequence = "harvest"; + + [Desc("Position relative to body")] + public readonly WVec Offset = WVec.Zero; + + public readonly string Palette = "effect"; + + public object Create(ActorInitializer init) { return new WithHarvestOverlay(init.Self, this); } + } + + class WithHarvestOverlay : INotifyHarvesterAction + { + WithHarvestOverlayInfo info; + Animation anim; + bool visible; + + public WithHarvestOverlay(Actor self, WithHarvestOverlayInfo info) + { + this.info = info; + var rs = self.Trait(); + var body = self.Trait(); + + anim = new Animation(self.World, rs.GetImage(self), RenderSprites.MakeFacingFunc(self)); + anim.IsDecoration = true; + anim.Play(info.Sequence); + rs.Add(new AnimationWithOffset(anim, + () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), + () => !visible, + () => false, + p => ZOffsetFromCenter(self, p, 0)), info.Palette); + } + + public void Harvested(Actor self, ResourceType resource) + { + if (visible) + return; + + visible = true; + anim.PlayThen(info.Sequence, () => visible = false); + } + + public void MovingToResources(Actor self, CPos targetCell, Activity next) { } + public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { } + public void MovementCancelled(Actor self) { } + public void Docked() { } + public void Undocked() { } + + public static int ZOffsetFromCenter(Actor self, WPos pos, int offset) + { + var delta = self.CenterPosition - pos; + return delta.Y + delta.Z + offset; + } + } +} diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 5dd5863784..880ce354c6 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -58,6 +58,8 @@ namespace OpenRA.Mods.Common.Traits void MovingToRefinery(Actor self, CPos targetCell, Activity next); void MovementCancelled(Actor self); void Harvested(Actor self, ResourceType resource); + void Docked(); + void Undocked(); } public interface ITechTreePrerequisite diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index ca08b7992b..ca3627dc08 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1221,6 +1221,136 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // Removed RenderUnit + if (engineVersion < 20150704) + { + // Renamed WithHarvestAnimation to WithHarvestOverlay + if (node.Key == "WithHarvestAnimation") + node.Key = "WithHarvestOverlay"; + + // Replaced RenderLandingCraft with WithFacingSpriteBody + WithLandingCraftAnimation. + // Note: These rules are set up to do approximately the right thing for maps, but + // mods might 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 childKeySequence = new[] { "Sequence" }; + var childKeysExcludeFromRS = new[] { "Sequence", "OpenTerrainTypes", "OpenSequence", "UnloadSequence" }; + + var rlc = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderLandingCraft")); + if (rlc != null) + { + rlc.Key = "WithLandingCraftAnimation"; + + var rsNodes = rlc.Value.Nodes.Where(n => !childKeysExcludeFromRS.Contains(n.Key)).ToList(); + var wfsbNodes = rlc.Value.Nodes.Where(n => childKeySequence.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", "")); + + // Note: For the RA landing craft WithSpriteBody would be sufficient since it has no facings, + // but WithFacingSpriteBody works as well and covers the potential case where a third-party mod + // might have given their landing craft multiple facings. + if (wfsbNodes.Any()) + node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", new MiniYaml("", wfsbNodes))); + else + node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", "")); + + node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", "")); + + rlc.Value.Nodes.RemoveAll(n => rsNodes.Contains(n)); + rlc.Value.Nodes.RemoveAll(n => wfsbNodes.Contains(n)); + } + + var rrlc = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderLandingCraft")); + if (rrlc != null) + rrlc.Key = "-WithLandingCraftAnimation"; + } + + // Replaced RenderHarvester with WithFacingSpriteBody + WithHarvestAnimation + WithDockingAnimation. + // Note: These rules are set up to do approximately the right thing for maps, but + // mods might 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 childKeySequence = new[] { "Sequence" }; + var childKeyIBF = new[] { "ImagesByFullness" }; + var childKeysExcludeFromRS = new[] { "Sequence", "ImagesByFullness", "HarvestSequence" }; + + var rh = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderHarvester")); + if (rh != null) + { + rh.Key = "WithHarvestAnimation"; + + var rsNodes = rh.Value.Nodes.Where(n => !childKeysExcludeFromRS.Contains(n.Key)).ToList(); + var wfsbNodes = rh.Value.Nodes.Where(n => childKeySequence.Contains(n.Key)).ToList(); + var ibfNode = rh.Value.Nodes.Where(n => childKeyIBF.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", "")); + + if (wfsbNodes.Any()) + node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", new MiniYaml("", wfsbNodes))); + else + node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", "")); + + node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", "")); + node.Value.Nodes.Add(new MiniYamlNode("WithDockingAnimation", "")); + + rh.Value.Nodes.RemoveAll(n => rsNodes.Contains(n)); + rh.Value.Nodes.RemoveAll(n => wfsbNodes.Contains(n)); + + if (ibfNode.Any()) + rh.Value.Nodes.RemoveAll(n => ibfNode.Contains(n)); + Console.WriteLine("The 'ImagesByFullness' property from the removed RenderHarvester trait has been"); + Console.WriteLine("replaced with a 'PrefixByFullness' property on the new WithHarvestAnimation trait."); + Console.WriteLine("This cannot be reliably upgraded, as the actor sequences need to be adapted as well."); + Console.WriteLine("Therefore, WithHarvestAnimation will use the default (no prefix) after upgrading."); + Console.WriteLine("See RA's harvester for reference on how to re-implement this feature using the new trait."); + } + + var rrh = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderHarvester")); + if (rrh != null) + rrh.Key = "-WithHarvestAnimation"; + } + + // Replace RenderUnit with RenderSprites + WithFacingSpriteBody + AutoSelectionSize. + // Normally this should have been removed by previous upgrade rules, but let's run this again + // to make sure to get rid of potential left-over cases like D2k sandworms and harvesters. + if (depth == 0) + { + var childKeys = new[] { "Sequence" }; + + var ru = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderUnit")); + if (ru != null) + { + ru.Key = "WithFacingSpriteBody"; + + 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", "")); + + node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", "")); + + ru.Value.Nodes.RemoveAll(n => rsNodes.Contains(n)); + + Console.WriteLine("RenderUnit has now been removed from code."); + Console.WriteLine("Use RenderSprites + WithFacingSpriteBody (+ AutoSelectionSize, if necessary) instead."); + } + + var rru = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderUnit")); + if (rru != null) + rru.Key = "-WithFacingSpriteBody"; + } + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/OpenRA.Mods.D2k/Activities/SwallowActor.cs b/OpenRA.Mods.D2k/Activities/SwallowActor.cs index 0701fff2e6..c92cb98503 100644 --- a/OpenRA.Mods.D2k/Activities/SwallowActor.cs +++ b/OpenRA.Mods.D2k/Activities/SwallowActor.cs @@ -29,7 +29,7 @@ namespace OpenRA.Mods.D2k.Activities readonly Target target; readonly Sandworm sandworm; readonly WeaponInfo weapon; - readonly RenderUnit renderUnit; + readonly WithSpriteBody withSpriteBody; readonly RadarPings radarPings; readonly AttackSwallow swallow; readonly IPositionable positionable; @@ -44,11 +44,11 @@ namespace OpenRA.Mods.D2k.Activities sandworm = self.Trait(); positionable = self.Trait(); swallow = self.Trait(); - renderUnit = self.Trait(); + withSpriteBody = self.Trait(); radarPings = self.World.WorldActor.TraitOrDefault(); countdown = swallow.Info.AttackTime; - renderUnit.DefaultAnimation.ReplaceAnim("burrowed"); + withSpriteBody.DefaultAnimation.ReplaceAnim(sandworm.Info.BurrowedSequence); stance = AttackState.Burrowed; location = target.Actor.Location; } @@ -104,7 +104,7 @@ namespace OpenRA.Mods.D2k.Activities // List because IEnumerable gets evaluated too late. void PlayAttack(Actor self, WPos attackPosition, List affectedPlayers) { - renderUnit.PlayCustomAnim(self, "mouth"); + withSpriteBody.PlayCustomAnimation(self, sandworm.Info.MouthSequence); Sound.Play(swallow.Info.WormAttackSound, self.CenterPosition); Game.RunAfterDelay(1000, () => @@ -142,7 +142,7 @@ namespace OpenRA.Mods.D2k.Activities self.World.AddFrameEndTask(w => self.Kill(self)); } else - renderUnit.DefaultAnimation.ReplaceAnim("idle"); + withSpriteBody.DefaultAnimation.ReplaceAnim(sandworm.Info.IdleSequence); return NextActivity; } @@ -156,7 +156,7 @@ namespace OpenRA.Mods.D2k.Activities if (!WormAttack(self)) { - renderUnit.DefaultAnimation.ReplaceAnim("idle"); + withSpriteBody.DefaultAnimation.ReplaceAnim(sandworm.Info.IdleSequence); return NextActivity; } diff --git a/OpenRA.Mods.D2k/Traits/Carryable.cs b/OpenRA.Mods.D2k/Traits/Carryable.cs index 30302305b5..ed9616507b 100644 --- a/OpenRA.Mods.D2k/Traits/Carryable.cs +++ b/OpenRA.Mods.D2k/Traits/Carryable.cs @@ -93,6 +93,8 @@ namespace OpenRA.Mods.D2k.Traits // We do not handle Harvested notification public void Harvested(Actor self, ResourceType resource) { } + public void Docked() { } + public void Undocked() { } public Actor GetClosestIdleCarrier() { diff --git a/OpenRA.Mods.D2k/Traits/Sandworm.cs b/OpenRA.Mods.D2k/Traits/Sandworm.cs index 3a88e181ee..8281a11d05 100644 --- a/OpenRA.Mods.D2k/Traits/Sandworm.cs +++ b/OpenRA.Mods.D2k/Traits/Sandworm.cs @@ -15,7 +15,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.D2k.Traits { - class SandwormInfo : WandersInfo, Requires, Requires, Requires + class SandwormInfo : WandersInfo, Requires, Requires, Requires { [Desc("Time between rescanning for targets (in ticks).")] public readonly int TargetRescanInterval = 32; @@ -30,7 +30,13 @@ namespace OpenRA.Mods.D2k.Traits public readonly int ChanceToDisappear = 80; [Desc("Name of the sequence that is used when the actor is idle or moving (not attacking).")] - public readonly string IdleSequence = "idle"; + [SequenceReference] public readonly string IdleSequence = "idle"; + + [Desc("Name of the sequence that is used when the actor is attacking.")] + [SequenceReference] public readonly string MouthSequence = "mouth"; + + [Desc("Name of the sequence that is used when the actor is burrowed.")] + [SequenceReference] public readonly string BurrowedSequence = "burrowed"; public override object Create(ActorInitializer init) { return new Sandworm(init.Self, this); } } @@ -41,7 +47,7 @@ namespace OpenRA.Mods.D2k.Traits readonly WormManager manager; readonly Lazy mobile; - readonly Lazy renderUnit; + readonly Lazy withSpriteBody; readonly Lazy attackTrait; public bool IsMovingTowardTarget { get; private set; } @@ -55,15 +61,15 @@ namespace OpenRA.Mods.D2k.Traits { Info = info; mobile = Exts.Lazy(self.Trait); - renderUnit = Exts.Lazy(self.Trait); + withSpriteBody = Exts.Lazy(self.Trait); attackTrait = Exts.Lazy(self.Trait); manager = self.World.WorldActor.Trait(); } public override void OnBecomingIdle(Actor self) { - if (renderUnit.Value.DefaultAnimation.CurrentSequence.Name != Info.IdleSequence) - renderUnit.Value.DefaultAnimation.PlayRepeating("idle"); + if (withSpriteBody.Value.DefaultAnimation.CurrentSequence.Name != Info.IdleSequence) + withSpriteBody.Value.DefaultAnimation.PlayRepeating(Info.IdleSequence); base.OnBecomingIdle(self); } diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 74e023c30b..4f91d64281 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -104,7 +104,7 @@ - + diff --git a/OpenRA.Mods.RA/Traits/Render/RenderLandingCraft.cs b/OpenRA.Mods.RA/Traits/Render/WithLandingCraftAnimation.cs similarity index 52% rename from OpenRA.Mods.RA/Traits/Render/RenderLandingCraft.cs rename to OpenRA.Mods.RA/Traits/Render/WithLandingCraftAnimation.cs index 27ed41eef2..740434f0f0 100644 --- a/OpenRA.Mods.RA/Traits/Render/RenderLandingCraft.cs +++ b/OpenRA.Mods.RA/Traits/Render/WithLandingCraftAnimation.cs @@ -14,30 +14,31 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Traits { - public class RenderLandingCraftInfo : RenderUnitInfo, Requires, Requires + public class WithLandingCraftAnimationInfo : ITraitInfo, Requires, Requires, Requires { public readonly string[] OpenTerrainTypes = { "Clear" }; - [SequenceReference] public readonly string OpenAnim = "open"; - [SequenceReference] public readonly string UnloadAnim = "unload"; + [SequenceReference] public readonly string OpenSequence = "open"; + [SequenceReference] public readonly string UnloadSequence = "unload"; - public override object Create(ActorInitializer init) { return new RenderLandingCraft(init, this); } + public object Create(ActorInitializer init) { return new WithLandingCraftAnimation(init, this); } } - public class RenderLandingCraft : RenderUnit + public class WithLandingCraftAnimation : ITick { - readonly RenderLandingCraftInfo info; + readonly WithLandingCraftAnimationInfo info; readonly Actor self; readonly Cargo cargo; readonly IMove move; + readonly WithSpriteBody wsb; bool open; - public RenderLandingCraft(ActorInitializer init, RenderLandingCraftInfo info) - : base(init, info) + public WithLandingCraftAnimation(ActorInitializer init, WithLandingCraftAnimationInfo info) { this.info = info; self = init.Self; cargo = self.Trait(); move = self.Trait(); + wsb = init.Self.Trait(); } public bool ShouldBeOpen() @@ -51,34 +52,32 @@ namespace OpenRA.Mods.RA.Traits void Open() { - if (open || !DefaultAnimation.HasSequence(info.OpenAnim)) + if (open || !wsb.DefaultAnimation.HasSequence(info.OpenSequence)) return; open = true; - PlayCustomAnimation(self, info.OpenAnim, () => + wsb.PlayCustomAnimation(self, info.OpenSequence, () => { - if (DefaultAnimation.HasSequence(info.UnloadAnim)) - PlayCustomAnimationRepeating(self, info.UnloadAnim); + if (wsb.DefaultAnimation.HasSequence(info.UnloadSequence)) + wsb.PlayCustomAnimationRepeating(self, info.UnloadSequence); }); } void Close() { - if (!open || !DefaultAnimation.HasSequence(info.OpenAnim)) + if (!open || !wsb.DefaultAnimation.HasSequence(info.OpenSequence)) return; open = false; - PlayCustomAnimationBackwards(self, info.OpenAnim, null); + wsb.PlayCustomAnimationBackwards(self, info.OpenSequence, null); } - public override void Tick(Actor self) + public void Tick(Actor self) { if (ShouldBeOpen()) Open(); else Close(); - - base.Tick(self); } } } diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index 62c48afeff..d768d3fb33 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -70,10 +70,8 @@ HARV: LeavesHusk: HuskActor: HARV.Husk -GainsExperience: - -RenderSprites: - -WithFacingSpriteBody: - -AutoSelectionSize: - RenderHarvester: + WithHarvestAnimation: + WithDockingAnimation: Explodes: Weapon: TiberiumExplosion SelectionDecorations: diff --git a/mods/d2k/rules/arrakis.yaml b/mods/d2k/rules/arrakis.yaml index 785394ab28..9f1c0d82d8 100644 --- a/mods/d2k/rules/arrakis.yaml +++ b/mods/d2k/rules/arrakis.yaml @@ -33,7 +33,7 @@ sandworm: Spice: 100 TargetableUnit: TargetTypes: Ground - RenderUnit: + WithFacingSpriteBody: WithAttackOverlay: Sequence: sand BodyOrientation: @@ -51,6 +51,8 @@ sandworm: AnnounceOnSeen: Notification: WormSign PingRadar: True + RenderSprites: + AutoSelectionSize: sietch: Inherits: ^Building diff --git a/mods/d2k/rules/starport.yaml b/mods/d2k/rules/starport.yaml index 7e9bf3c026..9ae4bc671a 100644 --- a/mods/d2k/rules/starport.yaml +++ b/mods/d2k/rules/starport.yaml @@ -14,7 +14,7 @@ harvester.starport: Queue: Starport Valued: Cost: 1500 - RenderUnit: + RenderSprites: Image: harvester trike.starport: diff --git a/mods/d2k/rules/vehicles.yaml b/mods/d2k/rules/vehicles.yaml index 6e063bdf4e..fccc46aed7 100644 --- a/mods/d2k/rules/vehicles.yaml +++ b/mods/d2k/rules/vehicles.yaml @@ -77,12 +77,9 @@ harvester: EmptyWeapon: UnitExplodeScale LeavesHusk: HuskActor: Harvester.Husk - RenderUnit: - -RenderSprites: - -WithFacingSpriteBody: - -AutoSelectionSize: - WithHarvestAnimation: + WithHarvestOverlay: Palette: effect50alpha + WithDockingAnimation: AttractsWorms: Intensity: 700 SelectionDecorations: diff --git a/mods/ra/maps/allies-05a/map.yaml b/mods/ra/maps/allies-05a/map.yaml index aca6603176..e5ae9d780d 100644 --- a/mods/ra/maps/allies-05a/map.yaml +++ b/mods/ra/maps/allies-05a/map.yaml @@ -1658,7 +1658,7 @@ Rules: ShowOwnerRow: false LST.IN: Inherits: LST - RenderLandingCraft: + RenderSprites: Image: LST Cargo: Types: disabled diff --git a/mods/ra/rules/ships.yaml b/mods/ra/rules/ships.yaml index 3508758e6e..88a30d1686 100644 --- a/mods/ra/rules/ships.yaml +++ b/mods/ra/rules/ships.yaml @@ -218,10 +218,7 @@ LST: Range: 6c0 SelectionDecorations: VisualBounds: 36,36 - -AutoSelectionSize: - -RenderSprites: - -WithFacingSpriteBody: - RenderLandingCraft: + WithLandingCraftAnimation: OpenTerrainTypes: Clear, Rough, Road, Ore, Gems, Beach Cargo: Types: Infantry, Vehicle diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index e2f193502e..e5718dd778 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -265,11 +265,9 @@ HARV: Crushes: wall, mine, crate, infantry RevealsShroud: Range: 4c0 - -WithFacingSpriteBody: - -AutoSelectionSize: - -RenderSprites: - RenderHarvester: - ImagesByFullness: harvempty, harvhalf, harv + WithHarvestAnimation: + PrefixByFullness: empty-, half-, full- + WithDockingAnimation: GpsDot: String: Harvester LeavesHusk: diff --git a/mods/ra/sequences/vehicles.yaml b/mods/ra/sequences/vehicles.yaml index 8f5a647dc3..464060a5e9 100644 --- a/mods/ra/sequences/vehicles.yaml +++ b/mods/ra/sequences/vehicles.yaml @@ -14,9 +14,27 @@ truk: icon: trukicon harv: - idle: + idle: harvempty Facings: 32 - harvest: + harvest: harvempty + Start: 32 + Length: 8 + Facings: 8 + empty-idle: harvempty + Facings: 32 + empty-harvest: harvempty + Start: 32 + Length: 8 + Facings: 8 + half-idle: harvhalf + Facings: 32 + half-harvest: harvhalf + Start: 32 + Length: 8 + Facings: 8 + full-idle: + Facings: 32 + full-harvest: Start: 32 Length: 8 Facings: 8 @@ -29,34 +47,6 @@ harv: icon: harvicon Start: 0 -harvhalf: - idle: - Facings: 32 - harvest: - Start: 32 - Length: 8 - Facings: 8 - dock: harv - Start: 96 - Length: 8 - dock-loop: harv - Start: 104 - Length: 7 - -harvempty: - idle: - Facings: 32 - harvest: - Start: 32 - Length: 8 - Facings: 8 - dock: harv - Start: 96 - Length: 8 - dock-loop: harv - Start: 104 - Length: 7 - hhusk: idle: Facings: 32 diff --git a/mods/ts/rules/shared-vehicles.yaml b/mods/ts/rules/shared-vehicles.yaml index 5bd05555a5..5582a185df 100644 --- a/mods/ts/rules/shared-vehicles.yaml +++ b/mods/ts/rules/shared-vehicles.yaml @@ -82,7 +82,7 @@ HARV: WithVoxelUnloadBody: Explodes: Weapon: TiberiumExplosion - WithHarvestAnimation: + WithHarvestOverlay: Offset: 384,0,0 Palette: effect SelectionDecorations: