diff --git a/OpenRA.Mods.Common/Effects/Rank.cs b/OpenRA.Mods.Common/Effects/Rank.cs deleted file mode 100644 index 4719fdc48a..0000000000 --- a/OpenRA.Mods.Common/Effects/Rank.cs +++ /dev/null @@ -1,67 +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.Collections.Generic; -using OpenRA.Effects; -using OpenRA.Graphics; -using OpenRA.Mods.Common.Traits; - -namespace OpenRA.Mods.Common.Effects -{ - class Rank : IEffect - { - readonly Actor self; - readonly Animation anim; - readonly string paletteName; - - public Rank(Actor self, string paletteName) - { - this.self = self; - this.paletteName = paletteName; - - var xp = self.Trait(); - anim = new Animation(self.World, "rank"); - anim.PlayRepeating("rank"); - anim.PlayFetchIndex("rank", () => xp.Level == 0 ? 0 : xp.Level - 1); - } - - public void Tick(World world) - { - if (self.IsDead) - world.AddFrameEndTask(w => w.Remove(this)); - else - anim.Tick(); - } - - public IEnumerable Render(WorldRenderer wr) - { - if (!self.IsInWorld) - yield break; - - if (self.IsDead) - yield break; - - if (!self.Owner.IsAlliedWith(self.World.RenderPlayer)) - yield break; - - if (wr.World.FogObscures(self)) - yield break; - - var pos = wr.ScreenPxPosition(self.CenterPosition); - var bounds = self.Bounds; - bounds.Offset(pos.X, pos.Y); - - var palette = wr.Palette(paletteName); - var offset = (int)(4 / wr.Viewport.Zoom); - var effectPos = wr.Position(new int2(bounds.Right - offset, bounds.Bottom - offset)); - yield return new SpriteRenderable(anim.Image, effectPos, WVec.Zero, 0, palette, 1f / wr.Viewport.Zoom, true); - } - } -} diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 1b6be932f6..11c6a4faea 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -143,7 +143,6 @@ - @@ -400,6 +399,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/GainsExperience.cs b/OpenRA.Mods.Common/Traits/GainsExperience.cs index 72abb9bc69..ed8c765eec 100644 --- a/OpenRA.Mods.Common/Traits/GainsExperience.cs +++ b/OpenRA.Mods.Common/Traits/GainsExperience.cs @@ -26,9 +26,6 @@ namespace OpenRA.Mods.Common.Traits "Value is a list of the upgrade types to grant")] public readonly Dictionary Upgrades = null; - [Desc("Palette for the chevron glyph rendered in the selection box.")] - public readonly string ChevronPalette = "effect"; - [Desc("Palette for the level up sprite.")] public readonly string LevelUpPalette = "effect"; @@ -42,10 +39,10 @@ namespace OpenRA.Mods.Common.Traits { return new Dictionary() { - { 200, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } }, - { 400, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } }, - { 800, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } }, - { 1600, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "eliteweapon", "selfheal" } } + { 200, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "rank" } }, + { 400, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "rank" } }, + { 800, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "rank" } }, + { 1600, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "rank", "eliteweapon", "selfheal" } } }; } @@ -108,15 +105,6 @@ namespace OpenRA.Mods.Common.Traits Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", "LevelUp", self.Owner.Country.Race); self.World.AddFrameEndTask(w => w.Add(new CrateEffect(self, "levelup", info.LevelUpPalette))); - - if (Level == 1) - { - self.World.AddFrameEndTask(w => - { - if (!self.IsDead) - w.Add(new Rank(self, info.ChevronPalette)); - }); - } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs index da624216fc..589d5bcdf2 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs @@ -15,6 +15,18 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { + [Flags] + public enum ReferencePoints + { + Top = 0, + VCenter = 1, + Bottom = 2, + + Left = 0 << 2, + HCenter = 1 << 2, + Right = 2 << 2, + } + [Desc("Displays a custom animation if conditions are satisfied.")] public class WithDecorationInfo : UpgradableTraitInfo, ITraitInfo { @@ -27,7 +39,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Palette to render the sprite in. Reference the world actor's PaletteFrom* traits.")] public readonly string Palette = "chrome"; - [Desc("Pixel offset relative to the top-left point of the actor's bounds.")] + [Desc("Point in the actor's bounding box used as reference for offsetting the decoration image." + + "Possible values are any combination of Top, VCenter, Bottom and Left, HCenter, Right separated by a comma.")] + public readonly ReferencePoints ReferencePoint = ReferencePoints.Top | ReferencePoints.Left; + + [Desc("Pixel offset relative to the actor's bounding box' reference point.")] public readonly int2 Offset = int2.Zero; [Desc("The Z offset to apply when rendering this decoration.")] @@ -61,6 +77,11 @@ namespace OpenRA.Mods.Common.Traits anim.PlayRepeating(info.Sequence); } + public void PlaySingleFrame(int frame) + { + anim.PlayFetchIndex(info.Sequence, () => frame); + } + public virtual bool ShouldRender(Actor self) { return true; } public IEnumerable Render(Actor self, WorldRenderer wr) @@ -91,11 +112,35 @@ namespace OpenRA.Mods.Common.Traits var pxPos = wr.ScreenPxPosition(self.CenterPosition); var actorBounds = self.Bounds; actorBounds.Offset(pxPos.X, pxPos.Y); - pxPos = new int2(actorBounds.Left, actorBounds.Top); var img = anim.Image; var imgSize = img.Size.ToInt2(); - pxPos = pxPos.WithX(pxPos.X + imgSize.X / 2).WithY(pxPos.Y + imgSize.Y / 2); + + switch (info.ReferencePoint & (ReferencePoints)3) + { + case ReferencePoints.Top: + pxPos = pxPos.WithY(actorBounds.Top + imgSize.Y / 2); + break; + case ReferencePoints.VCenter: + pxPos = pxPos.WithY((actorBounds.Top + actorBounds.Bottom) / 2); + break; + case ReferencePoints.Bottom: + pxPos = pxPos.WithY(actorBounds.Bottom - imgSize.Y / 2); + break; + } + + switch (info.ReferencePoint & (ReferencePoints)(3 << 2)) + { + case ReferencePoints.Left: + pxPos = pxPos.WithX(actorBounds.Left + imgSize.X / 2); + break; + case ReferencePoints.HCenter: + pxPos = pxPos.WithX((actorBounds.Left + actorBounds.Right) / 2); + break; + case ReferencePoints.Right: + pxPos = pxPos.WithX(actorBounds.Right - imgSize.X / 2); + break; + } pxPos += info.Offset; var renderPos = wr.Position(pxPos); diff --git a/OpenRA.Mods.Common/Traits/Render/WithRankDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithRankDecoration.cs new file mode 100644 index 0000000000..a9a5f65f3f --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithRankDecoration.cs @@ -0,0 +1,27 @@ +#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 + +namespace OpenRA.Mods.Common.Traits.Render +{ + public class WithRankDecorationInfo : WithDecorationInfo + { + public override object Create(ActorInitializer init) { return new WithRankDecoration(init.Self, this); } + } + + public class WithRankDecoration : WithDecoration + { + public WithRankDecoration(Actor self, WithRankDecorationInfo info) : base(self, info) { } + + protected override void UpgradeLevelChanged(Actor self, int oldLevel, int newLevel) + { + PlaySingleFrame(newLevel - 1); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 09ffe25d62..0b835a58aa 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1085,6 +1085,40 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20150607) + { + // Add WithRankDecoration to all actors using GainsExperience + var ge = node.Value.Nodes.FirstOrDefault(n => n.Key == "GainsExperience"); + if (ge != null) + { + var nodeUpgrades = ge.Value.Nodes.FirstOrDefault(n => n.Key == "Upgrades"); + var upgrades = nodeUpgrades != null ? nodeUpgrades.Value.Nodes.Count() : 4; + + var nodeChPal = ge.Value.Nodes.FirstOrDefault(n => n.Key == "ChevronPalette"); + var chPal = nodeChPal != null && !string.IsNullOrEmpty(nodeChPal.Value.Value) ? nodeChPal.Value.Value : "effect"; + ge.Value.Nodes.Remove(nodeChPal); + + if (upgrades != 0) + { + foreach (var nodeUpgrade in nodeUpgrades.Value.Nodes) + nodeUpgrade.Value.Value = "rank" + (string.IsNullOrEmpty(nodeUpgrade.Value.Value) ? null : ", ") + nodeUpgrade.Value.Value; + + node.Value.Nodes.Add(new MiniYamlNode("WithRankDecoration", null, new List + { + new MiniYamlNode("Image", "rank"), + new MiniYamlNode("Sequence", "rank"), + new MiniYamlNode("Palette", chPal), + new MiniYamlNode("ReferencePoint", "Bottom, Right"), + new MiniYamlNode("Offset", "2, 2"), + new MiniYamlNode("UpgradeTypes", "rank"), + new MiniYamlNode("ZOffset", "256"), + new MiniYamlNode("UpgradeMinEnabledLevel", "1"), + new MiniYamlNode("UpgradeMaxAcceptedLevel", upgrades.ToString()) + })); + } + } + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 2988e028a3..9c96c4b4f9 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -1,4 +1,26 @@ +^GainsExperience: + GainsExperience: + GainsStatUpgrades: + SelfHealing@ELITE: + Step: 2 + Ticks: 100 + HealIfBelow: 1 + DamageCooldown: 125 + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 + WithRankDecoration: + Image: rank + Sequence: rank + Palette: effect + ReferencePoint: Bottom, Right + Offset: 2, 2 + UpgradeTypes: rank + ZOffset: 256 + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 4 + ^Vehicle: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: crate @@ -18,7 +40,6 @@ Passenger: CargoType: Vehicle HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -40,20 +61,13 @@ UncloakSound: trans1.aud Huntable: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: MustBeDestroyed: Voiced: VoiceSet: VehicleVoice ^Tank: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: wall, crate, infantry @@ -73,7 +87,6 @@ Passenger: CargoType: Vehicle HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -98,20 +111,13 @@ UncloakSound: trans1.aud Huntable: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: MustBeDestroyed: Voiced: VoiceSet: VehicleVoice ^Helicopter: + Inherits: ^GainsExperience AppearsOnRadar: UseLocation: yes TargetableAircraft: @@ -124,7 +130,6 @@ RearmBuildings: LandWhenIdle: false HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -141,14 +146,6 @@ ScriptTriggers: Tooltip: GenericName: Helicopter - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: WithShadow: Hovers: @@ -157,6 +154,7 @@ VoiceSet: VehicleVoice ^Infantry: + Inherits: ^GainsExperience AppearsOnRadar: Health: Radius: 128 @@ -203,7 +201,6 @@ CargoType: Infantry HiddenUnderFog: PoisonedByTiberium: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -241,14 +238,6 @@ DeathSounds@POISONED: DeathSound: Poisoned DeathTypes: TiberiumDeath - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: MustBeDestroyed: Voiced: @@ -335,6 +324,7 @@ VoiceSet: DinoVoice ^Plane: + Inherits: ^GainsExperience AppearsOnRadar: UseLocation: yes SelectionDecorations: @@ -342,7 +332,6 @@ TargetableUnit: TargetTypes: Air HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -351,20 +340,13 @@ Huntable: AttackMove: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: WithShadow: Voiced: VoiceSet: GenericVoice ^Ship: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: crate @@ -375,7 +357,6 @@ TargetableUnit: TargetTypes: Ground, Water HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -387,14 +368,6 @@ UpdatesPlayerStatistics: Huntable: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: Voiced: VoiceSet: VehicleVoice diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 220497ec6b..88f32fcc80 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -1,4 +1,26 @@ +^GainsExperience: + GainsExperience: + GainsStatUpgrades: + SelfHealing@ELITE: + Step: 2 + Ticks: 100 + HealIfBelow: 1 + DamageCooldown: 125 + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 + WithRankDecoration: + Image: rank + Sequence: rank + Palette: effect + ReferencePoint: Bottom, Right + Offset: 2, 2 + UpgradeTypes: rank + ZOffset: 256 + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 4 + ^Vehicle: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: crate @@ -19,7 +41,6 @@ CargoType: Vehicle AttackMove: HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -38,14 +59,6 @@ Huntable: Demolishable: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: TemporaryOwnerManager: MustBeDestroyed: @@ -62,6 +75,7 @@ RenderSprites: ^Tank: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: crate @@ -82,7 +96,6 @@ CargoType: Vehicle AttackMove: HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -101,14 +114,6 @@ Huntable: Demolishable: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: TemporaryOwnerManager: MustBeDestroyed: @@ -187,6 +192,7 @@ Explosion: UnitExplodeScale ^Infantry: + Inherits: ^GainsExperience AppearsOnRadar: Health: Radius: 96 @@ -229,7 +235,6 @@ CargoType: Infantry PipType: Green HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -250,14 +255,6 @@ DeathTypes: ExplosionDeath, SoundDeath, SmallExplosionDeath, BulletDeath Parachutable: FallRate: 130 - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: MustBeDestroyed: AnnounceOnSeen: @@ -269,6 +266,7 @@ VoiceSet: InfantryVoice ^Plane: + Inherits: ^GainsExperience AppearsOnRadar: UseLocation: yes SelectionDecorations: @@ -277,7 +275,6 @@ TargetTypes: Air GroundedTargetTypes: Ground HiddenUnderFog: - GainsExperience: GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -290,14 +287,6 @@ Huntable: AttackMove: ScriptTriggers: - GainsStatUpgrades: - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 UpgradeManager: AnnounceOnSeen: Notification: EnemyUnitsDetected diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 79a9940f5e..5d71d883c8 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -19,6 +19,16 @@ DamageCooldown: 125 UpgradeTypes: selfheal UpgradeMinEnabledLevel: 1 + WithRankDecoration: + Image: rank + Sequence: rank + Palette: effect + ReferencePoint: Bottom, Right + Offset: 2, 2 + UpgradeTypes: rank + ZOffset: 256 + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 4 ^IronCurtainable: UpgradeOverlay@IRONCURTAIN: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index e3eaceea48..95c5d57726 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -1,3 +1,31 @@ +^GainsExperience: + GainsExperience: + Upgrades: + 500: rank, firepower, damage, speed, reload + 1000: rank, firepower, damage, speed, reload, selfheal, eliteweapon + GainsStatUpgrades: + FirepowerModifier: 110, 130 + DamageModifier: 83, 66 + SpeedModifier: 120, 150 + ReloadModifier: 90, 75 + SelfHealing@ELITE: + Step: 2 + Ticks: 100 + HealIfBelow: 1 + DamageCooldown: 125 + UpgradeTypes: selfheal + UpgradeMinEnabledLevel: 1 + WithRankDecoration: + Image: rank + Sequence: rank + Palette: ra + ReferencePoint: Bottom, Right + Offset: 2, 2 + UpgradeTypes: rank + ZOffset: 256 + UpgradeMinEnabledLevel: 1 + UpgradeMaxAcceptedLevel: 2 + ^Building: AppearsOnRadar: SelectionDecorations: @@ -164,6 +192,7 @@ RenderSprites: ^Infantry: + Inherits: ^GainsExperience AppearsOnRadar: Health: Radius: 128 @@ -213,23 +242,6 @@ Passenger: CargoType: Infantry HiddenUnderFog: - GainsExperience: - ChevronPalette: ra - Upgrades: - 500: firepower, damage, speed, reload - 1000: firepower, damage, speed, reload, selfheal, eliteweapon - GainsStatUpgrades: - FirepowerModifier: 110, 130 - DamageModifier: 83, 66 - SpeedModifier: 120, 150 - ReloadModifier: 90, 75 - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -289,6 +301,7 @@ -MustBeDestroyed: ^Vehicle: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: crate @@ -314,23 +327,6 @@ CargoType: Vehicle AttackMove: HiddenUnderFog: - GainsExperience: - ChevronPalette: ra - Upgrades: - 500: firepower, damage, speed, reload - 1000: firepower, damage, speed, reload, selfheal, eliteweapon - GainsStatUpgrades: - FirepowerModifier: 110, 130 - DamageModifier: 83, 66 - SpeedModifier: 120, 150 - ReloadModifier: 90, 75 - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -374,6 +370,7 @@ MustBeDestroyed: ^Tank: + Inherits: ^GainsExperience AppearsOnRadar: Mobile: Crushes: wall, crate @@ -399,23 +396,6 @@ CargoType: Vehicle AttackMove: HiddenUnderFog: - GainsExperience: - ChevronPalette: ra - Upgrades: - 500: firepower, damage, speed, reload - 1000: firepower, damage, speed, reload, selfheal, eliteweapon - GainsStatUpgrades: - FirepowerModifier: 110, 130 - DamageModifier: 83, 66 - SpeedModifier: 120, 150 - ReloadModifier: 90, 75 - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: @@ -459,6 +439,7 @@ MustBeDestroyed: ^Helicopter: + Inherits: ^GainsExperience AppearsOnRadar: UseLocation: yes TargetableAircraft: @@ -476,23 +457,6 @@ CruiseAltitude: 2048 HiddenUnderFog: AttackMove: - GainsExperience: - ChevronPalette: ra - Upgrades: - 500: firepower, damage, speed, reload - 1000: firepower, damage, speed, reload, selfheal, eliteweapon - GainsStatUpgrades: - FirepowerModifier: 110, 130 - DamageModifier: 83, 66 - SpeedModifier: 120, 150 - ReloadModifier: 90, 75 - SelfHealing@ELITE: - Step: 2 - Ticks: 100 - HealIfBelow: 1 - DamageCooldown: 125 - UpgradeTypes: selfheal - UpgradeMinEnabledLevel: 1 GivesExperience: DrawLineToTarget: ActorLostNotification: