diff --git a/OpenRA.Mods.Common/Activities/Move/Move.cs b/OpenRA.Mods.Common/Activities/Move/Move.cs index ebaf66aeb9..e5a704d33a 100644 --- a/OpenRA.Mods.Common/Activities/Move/Move.cs +++ b/OpenRA.Mods.Common/Activities/Move/Move.cs @@ -142,6 +142,9 @@ namespace OpenRA.Mods.Common.Activities public override Activity Tick(Actor self) { + if (IsCanceled) + return NextActivity; + if (moveDisablers.Any(d => d.MoveDisabled(self))) return this; diff --git a/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs b/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs index 7cd11fa474..1d1db6b299 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs @@ -18,11 +18,11 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Also returns a default selection size that is calculated automatically from the voxel dimensions.")] - public class WithVoxelBodyInfo : ITraitInfo, IRenderActorPreviewVoxelsInfo, Requires + public class WithVoxelBodyInfo : UpgradableTraitInfo, IRenderActorPreviewVoxelsInfo, Requires { public readonly string Sequence = "idle"; - public object Create(ActorInitializer init) { return new WithVoxelBody(init.Self, this); } + public override object Create(ActorInitializer init) { return new WithVoxelBody(init.Self, this); } public IEnumerable RenderPreviewVoxels(ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, WRot orientation, int facings, PaletteReference p) { @@ -35,11 +35,12 @@ namespace OpenRA.Mods.Common.Traits } } - public class WithVoxelBody : IAutoSelectionSize + public class WithVoxelBody : UpgradableTrait, IAutoSelectionSize { readonly int2 size; public WithVoxelBody(Actor self, WithVoxelBodyInfo info) + : base(info) { var body = self.Trait(); var rv = self.Trait(); @@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits var voxel = VoxelProvider.GetVoxel(rv.Image, info.Sequence); rv.Add(new VoxelAnimation(voxel, () => WVec.Zero, () => new[] { body.QuantizeOrientation(self, self.Orientation) }, - () => false, () => 0)); + () => IsTraitDisabled, () => 0)); // Selection size var rvi = self.Info.Traits.Get(); diff --git a/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs b/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs index 3599c9883e..f9b7f23fee 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs @@ -11,6 +11,8 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Activities; +using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Traits; @@ -32,6 +34,18 @@ namespace OpenRA.Mods.Common.Traits [Desc("Cursor to display when unable to (un)deploy the actor.")] public readonly string DeployBlockedCursor = "deploy-blocked"; + [SequenceReference, Desc("Animation to play for deploying/undeploying.")] + public readonly string DeployAnimation = null; + + [Desc("Facing that the actor must face before deploying. Set to -1 to deploy regardless of facing.")] + public readonly int Facing = -1; + + [Desc("Sound to play when deploying.")] + public readonly string DeploySound = null; + + [Desc("Sound to play when undeploying.")] + public readonly string UndeploySound = null; + public object Create(ActorInitializer init) { return new DeployToUpgrade(init.Self, this); } } @@ -41,6 +55,8 @@ namespace OpenRA.Mods.Common.Traits readonly DeployToUpgradeInfo info; readonly UpgradeManager manager; readonly bool checkTerrainType; + readonly bool canTurn; + readonly Lazy body; bool isUpgraded; @@ -50,6 +66,8 @@ namespace OpenRA.Mods.Common.Traits this.info = info; manager = self.Trait(); checkTerrainType = info.AllowedTerrainTypes.Length > 0; + canTurn = self.Info.Traits.WithInterface().Any(); + body = Exts.Lazy(self.TraitOrDefault); } public IEnumerable Orders @@ -75,11 +93,50 @@ namespace OpenRA.Mods.Common.Traits return; if (isUpgraded) - foreach (var up in info.Upgrades) - manager.RevokeUpgrade(self, up, this); + { + // Play undeploy animation and after that revoke the upgrades + self.QueueActivity(false, new CallFunc(() => + { + if (!string.IsNullOrEmpty(info.UndeploySound)) + Sound.Play(info.UndeploySound, self.CenterPosition); + + if (string.IsNullOrEmpty(info.DeployAnimation)) + { + RevokeUpgrades(); + return; + } + + if (body.Value != null) + body.Value.PlayCustomAnimationBackwards(self, info.DeployAnimation, RevokeUpgrades); + else + RevokeUpgrades(); + })); + } else - foreach (var up in info.Upgrades) - manager.GrantUpgrade(self, up, this); + { + self.CancelActivity(); + + // Turn + if (info.Facing != -1 && canTurn) + self.QueueActivity(new Turn(self, info.Facing)); + + // Grant the upgrade + self.QueueActivity(new CallFunc(GrantUpgrades)); + + // Play deploy sound and animation + self.QueueActivity(new CallFunc(() => + { + if (!string.IsNullOrEmpty(info.DeploySound)) + Sound.Play(info.DeploySound, self.CenterPosition); + + if (string.IsNullOrEmpty(info.DeployAnimation)) + return; + + if (body.Value != null) + body.Value.PlayCustomAnimation(self, info.DeployAnimation, + () => body.Value.PlayCustomAnimationRepeating(self, "idle")); + })); + } isUpgraded = !isUpgraded; } @@ -98,5 +155,17 @@ namespace OpenRA.Mods.Common.Traits return info.AllowedTerrainTypes.Contains(terrainType); } + + void GrantUpgrades() + { + foreach (var up in info.Upgrades) + manager.GrantUpgrade(self, up, this); + } + + void RevokeUpgrades() + { + foreach (var up in info.Upgrades) + manager.RevokeUpgrade(self, up, this); + } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Upgrades/DisableUpgrade.cs b/OpenRA.Mods.Common/Traits/Upgrades/DisableUpgrade.cs index 2e07036146..b02beb2978 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/DisableUpgrade.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/DisableUpgrade.cs @@ -8,13 +8,11 @@ */ #endregion -using System; -using System.Collections.Generic; -using OpenRA.GameRules; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { + [Desc("Disable the actor when this trait is enabled by an upgrade.")] public class DisableUpgradeInfo : UpgradableTraitInfo { public override object Create(ActorInitializer init) { return new DisableUpgrade(this); } @@ -25,7 +23,6 @@ namespace OpenRA.Mods.Common.Traits public DisableUpgrade(DisableUpgradeInfo info) : base(info) { } - // Disable the actor when this trait is enabled. public bool Disabled { get { return !IsTraitDisabled; } } public bool MoveDisabled(Actor self) { return !IsTraitDisabled; } } diff --git a/mods/ts/rules/shared-vehicles.yaml b/mods/ts/rules/shared-vehicles.yaml index 215df07cfc..4da98920c8 100644 --- a/mods/ts/rules/shared-vehicles.yaml +++ b/mods/ts/rules/shared-vehicles.yaml @@ -100,35 +100,54 @@ HARV: LPST: Inherits: ^VoxelVehicle + -AppearsOnRadar: + -GainsExperience: + Buildable: + Queue: Vehicle + BuildPaletteOrder: 100 + Prerequisites: ~factory, radar Valued: Cost: 950 Tooltip: Name: Mobile Sensor Array Description: Can detect cloaked and subterranean\nunits when deployed.\n Unarmed - Buildable: - Queue: Vehicle - BuildPaletteOrder: 100 - Prerequisites: ~factory, radar Health: HP: 600 Armor: - Type: Light + Type: Wood Mobile: Speed: 85 ROT: 5 RevealsShroud: - Range: 10c0 - Transforms: - IntoActor: gadpsa - Facing: 159 - TransformSounds: - NoTransformSounds: - Voice: Move + Range: 7c0 RenderSprites: Image: lpst.gdi FactionImages: gdi: lpst.gdi nod: lpst.nod + DeployToUpgrade: + Upgrades: deployed + DeployAnimation: make + Facing: 160 + AllowedTerrainTypes: Clear, Road, DirtRoad, Rough + DeploySound: place2.aud + UndeploySound: clicky1.aud + WithVoxelBody: + Image: lpst + UpgradeTypes: deployed + UpgradeMaxEnabledLevel: 0 + WithSpriteBody@deployed: + StartSequence: make + UpgradeTypes: deployed + UpgradeMinEnabledLevel: 1 + DisableUpgrade: + UpgradeTypes: deployed + UpgradeMinEnabledLevel: 1 + DetectCloaked: + UpgradeTypes: deployed + UpgradeMinEnabledLevel: 1 + Range: 18 + RenderDetectionCircle: GGHUNT: Inherits: ^Vehicle diff --git a/mods/ts/sequences/vehicles.yaml b/mods/ts/sequences/vehicles.yaml index f54745f0e2..671610484a 100644 --- a/mods/ts/sequences/vehicles.yaml +++ b/mods/ts/sequences/vehicles.yaml @@ -26,9 +26,23 @@ hvr: lpst.gdi: icon: sidec01.mix:lpsticon + idle: gadpsa + Offset: 0, -12 + ShadowStart: 3 + make: gadpsamk + Offset: 0, -12 + Length: 36 + ShadowStart: 36 lpst.nod: icon: sidec02.mix:lpsticon + idle: gadpsa + Offset: 0, -12 + ShadowStart: 3 + make: gadpsamk + Offset: 0, -12 + Length: 36 + ShadowStart: 36 repair: icon: rboticon