diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 46d96e8281..167eb26aa1 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -443,6 +443,8 @@ + + diff --git a/OpenRA.Mods.Common/Traits/Render/WithNukeLaunchAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithNukeLaunchAnimation.cs new file mode 100644 index 0000000000..853681ffbf --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithNukeLaunchAnimation.cs @@ -0,0 +1,54 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits.Render +{ + [Desc("Replaces the building animation when `NukePower` is triggered.")] + public class WithNukeLaunchAnimationInfo : ConditionalTraitInfo, Requires + { + [Desc("Sequence name to use")] + [SequenceReference] public readonly string Sequence = "active"; + + public override object Create(ActorInitializer init) { return new WithNukeLaunchAnimation(init.Self, this); } + } + + public class WithNukeLaunchAnimation : ConditionalTrait, INotifyNuke, INotifyBuildComplete, INotifySold + { + readonly WithSpriteBody spriteBody; + bool buildComplete; + + public WithNukeLaunchAnimation(Actor self, WithNukeLaunchAnimationInfo info) + : base(info) + { + spriteBody = self.TraitOrDefault(); + } + + void INotifyNuke.Launching(Actor self) + { + if (buildComplete && spriteBody != null && !IsTraitDisabled) + spriteBody.PlayCustomAnimation(self, Info.Sequence, () => spriteBody.CancelCustomAnimation(self)); + } + + void INotifyBuildComplete.BuildingComplete(Actor self) + { + buildComplete = true; + } + + void INotifySold.Selling(Actor self) + { + buildComplete = false; + } + + void INotifySold.Sold(Actor self) { } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Render/WithNukeLaunchOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithNukeLaunchOverlay.cs new file mode 100644 index 0000000000..ef5c8d86c5 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithNukeLaunchOverlay.cs @@ -0,0 +1,77 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Effects; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits.Render +{ + [Desc("Displays an overlay when `NukePower` is triggered.")] + public class WithNukeLaunchOverlayInfo : ConditionalTraitInfo, Requires, Requires + { + [Desc("Sequence name to use")] + [SequenceReference] public readonly string Sequence = "active"; + + [Desc("Position relative to body")] + public readonly WVec Offset = WVec.Zero; + + [Desc("Custom palette name")] + [PaletteReference("IsPlayerPalette")] public readonly string Palette = null; + + [Desc("Custom palette is a player palette BaseName")] + public readonly bool IsPlayerPalette = false; + + public override object Create(ActorInitializer init) { return new WithNukeLaunchOverlay(init.Self, this); } + } + + public class WithNukeLaunchOverlay : ConditionalTrait, INotifyBuildComplete, INotifySold, INotifyNuke + { + readonly Animation overlay; + bool buildComplete; + bool visible; + + public WithNukeLaunchOverlay(Actor self, WithNukeLaunchOverlayInfo info) + : base(info) + { + var rs = self.Trait(); + var body = self.Trait(); + + buildComplete = !self.Info.HasTraitInfo(); // always render instantly for units + overlay = new Animation(self.World, rs.GetImage(self)); + overlay.PlayThen(info.Sequence, () => visible = false); + + var anim = new AnimationWithOffset(overlay, + () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), + () => IsTraitDisabled || !visible || !buildComplete, + p => RenderUtils.ZOffsetFromCenter(self, p, 1)); + + rs.Add(anim, info.Palette, info.IsPlayerPalette); + } + + void INotifyBuildComplete.BuildingComplete(Actor self) + { + buildComplete = true; + } + + void INotifySold.Sold(Actor self) { } + void INotifySold.Selling(Actor self) + { + buildComplete = false; + } + + void INotifyNuke.Launching(Actor self) + { + visible = true; + overlay.PlayThen(overlay.CurrentSequence.Name, () => visible = false); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs index 44dd936059..05c13c01fb 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs @@ -72,10 +72,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("Corresponds to `Type` from `FlashPaletteEffect` on the world actor.")] public readonly string FlashType = null; - [SequenceReference] - [Desc("Sequence the launching actor should play when activating this power.")] - public readonly string ActivationSequence = "active"; - public WeaponInfo WeaponInfo { get; private set; } public override object Create(ActorInitializer init) { return new NukePower(init.Self, this); } @@ -103,11 +99,8 @@ namespace OpenRA.Mods.Common.Traits base.Activate(self, order, manager); PlayLaunchSounds(); - if (!string.IsNullOrEmpty(info.ActivationSequence)) - { - var wsb = self.Trait(); - wsb.PlayCustomAnimation(self, info.ActivationSequence); - } + foreach (var launchpad in self.TraitsImplementing()) + launchpad.Launching(self); var targetPosition = self.World.Map.CenterOfCell(order.TargetLocation); var palette = info.IsPlayerPalette ? info.MissilePalette + self.Owner.InternalName : info.MissilePalette; diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 0445f590db..4decc747c7 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -84,6 +84,7 @@ namespace OpenRA.Mods.Common.Traits public interface INotifyBuildingPlaced { void BuildingPlaced(Actor self); } public interface INotifyRepair { void Repairing(Actor self, Actor target); } + public interface INotifyNuke { void Launching(Actor self); } public interface INotifyBurstComplete { void FiredBurst(Actor self, Target target, Armament a); } public interface INotifyChat { bool OnChat(string from, string message); } public interface INotifyProduction { void UnitProduced(Actor self, Actor other, CPos exit); } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index c98a1cbb6d..64ec434114 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -974,6 +974,16 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // nuke launch animation is now it's own trait + if (engineVersion < 20170820) + { + if (depth == 1 && node.Key.StartsWith("NukePower")) + { + node.Value.Nodes.RemoveAll(n => n.Key == "ActivationSequence"); + addNodes.Add(new MiniYamlNode("WithNukeLaunchAnimation", new MiniYaml(""))); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index 59c5f5e805..1dda026f5f 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -724,6 +724,7 @@ TMPL: ArrowSequence: arrow ClockSequence: clock CircleSequence: circles + WithNukeLaunchAnimation: SupportPowerChargeBar: Power: Amount: -150 diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index 44e80cbbed..de0d817b34 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -1072,9 +1072,13 @@ palace: DisplayBeacon: True DisplayRadarPing: True CameraRange: 10c0 - ActivationSequence: ArrowSequence: arrow CircleSequence: circles + WithNukeLaunchOverlay: + RequiresCondition: !launchpad-damaged + GrantConditionOnDamageState@LAUNCHPADDAMAGED: + Condition: launchpad-damaged + ValidDamageState: Medium, Heavy, Critical ProduceActorPower@fremen: Description: Recruit Fremen LongDesc: Elite infantry unit armed with assault rifles and rockets\n Strong vs Infantry, Vehicles\n Weak vs Artillery\n Special Ability: Invisibility diff --git a/mods/d2k/sequences/structures.yaml b/mods/d2k/sequences/structures.yaml index c7dc3b5941..fd1c05cd0a 100644 --- a/mods/d2k/sequences/structures.yaml +++ b/mods/d2k/sequences/structures.yaml @@ -566,6 +566,9 @@ palace.atreides: idle: DATA.R8 Start: 2924 Offset: -48,48 + active: DATA.R8 + Start: 2924 + Offset: -48,48 make: DATA.R8 Start: 4677 Length: 11 @@ -980,10 +983,10 @@ palace.harkonnen: damaged-idle: DATA.R8 Start: 3085 Offset: -48,48 -# active: DATA.R8 # TODO: overlay -# Start: 5084 -# Length: 20 -# Offset: -48,64 + active: DATA.R8 + Start: 5084 + Length: 20 + Offset: -48,64 damaged-active: DATA.R8 Start: 5084 Length: 20 @@ -1371,6 +1374,9 @@ palace.ordos: idle: DATA.R8 Start: 3244 Offset: -48,48 + active: DATA.R8 + Start: 3244 + Offset: -48,48 make: DATA.R8 Start: 4677 Length: 11 @@ -1485,6 +1491,9 @@ palace.corrino: idle: DATA.R8 Start: 3252 Offset: -48,48 + active: DATA.R8 + Start: 3252 + Offset: -48,48 damaged-idle: DATA.R8 Start: 3253 Offset: -48,48 diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index fc1608bffd..586837b55e 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -61,6 +61,7 @@ MSLO: Amount: -150 MustBeDestroyed: RequiredForShortGame: false + WithNukeLaunchAnimation: GAP: Inherits: ^ScienceBuilding diff --git a/mods/ts/rules/nod-support.yaml b/mods/ts/rules/nod-support.yaml index 295f4b2a03..c517fccdc0 100644 --- a/mods/ts/rules/nod-support.yaml +++ b/mods/ts/rules/nod-support.yaml @@ -378,6 +378,7 @@ NAMISL: DisplayRadarPing: True BeaconPoster: CameraRange: 10c0 + WithNukeLaunchOverlay: SelectionDecorations: VisualBounds: 75,48 diff --git a/mods/ts/sequences/structures.yaml b/mods/ts/sequences/structures.yaml index b96c8a2d0f..59c472fa8e 100644 --- a/mods/ts/sequences/structures.yaml +++ b/mods/ts/sequences/structures.yaml @@ -1871,10 +1871,10 @@ namisl: make: ntmislmk Length: 18 ShadowStart: 18 - active: ntmisl_a # TODO: This is an overlay, but currently used as animation + active: ntmisl_a Length: 10 Tick: 80 - damaged-active: ntmisl_a # TODO: This is an overlay, but currently used as animation + damaged-active: ntmisl_a Start: 10 Length: 10 Tick: 80