diff --git a/OpenRA.Mods.Common/Activities/Demolish.cs b/OpenRA.Mods.Common/Activities/Demolish.cs index c70069a064..587ede8fcb 100644 --- a/OpenRA.Mods.Common/Activities/Demolish.cs +++ b/OpenRA.Mods.Common/Activities/Demolish.cs @@ -54,7 +54,7 @@ namespace OpenRA.Mods.Common.Activities if (target.IsDead) return; - if (cloak != null && cloak.Info.UncloakOnDemolish) + if (cloak != null && cloak.Info.UncloakOn.HasFlag(UncloakType.Demolish)) cloak.Uncloak(); for (var f = 0; f < flashes; f++) diff --git a/OpenRA.Mods.Common/Activities/UnloadCargo.cs b/OpenRA.Mods.Common/Activities/UnloadCargo.cs index a7807c832f..2c152d9a6f 100644 --- a/OpenRA.Mods.Common/Activities/UnloadCargo.cs +++ b/OpenRA.Mods.Common/Activities/UnloadCargo.cs @@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Activities if (IsCanceled || cargo.IsEmpty(self)) return NextActivity; - if (cloak != null && cloak.Info.UncloakOnUnload) + if (cloak != null && cloak.Info.UncloakOn.HasFlag(UncloakType.Unload)) cloak.Uncloak(); var actor = cargo.Peek(self); diff --git a/OpenRA.Mods.Common/Traits/Cloak.cs b/OpenRA.Mods.Common/Traits/Cloak.cs index 783d476423..2bdf7a8adb 100644 --- a/OpenRA.Mods.Common/Traits/Cloak.cs +++ b/OpenRA.Mods.Common/Traits/Cloak.cs @@ -17,6 +17,18 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { + [Flags] + public enum UncloakType + { + None = 0, + Attack = 1, + Move = 2, + Unload = 4, + Infiltrate = 8, + Demolish = 16, + Damage = 32 + } + [Desc("This unit can cloak and uncloak in specific situations.")] public class CloakInfo : UpgradableTraitInfo { @@ -26,11 +38,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Measured in game ticks.")] public readonly int CloakDelay = 30; - public readonly bool UncloakOnAttack = true; - public readonly bool UncloakOnMove = false; - public readonly bool UncloakOnUnload = true; - public readonly bool UncloakOnInfiltrate = true; - public readonly bool UncloakOnDemolish = true; + [Desc("Events leading to the actor getting uncloaked. Possible values are: Attack, Move, Unload, Infiltrate, Demolish and Damage")] + public readonly UncloakType UncloakOn = UncloakType.Attack + | UncloakType.Unload | UncloakType.Infiltrate | UncloakType.Demolish; public readonly string CloakSound = null; public readonly string UncloakSound = null; @@ -44,70 +54,48 @@ namespace OpenRA.Mods.Common.Traits [Desc("The upgrades to grant to self while cloaked.")] public readonly string[] WhileCloakedUpgrades = { }; - public override object Create(ActorInitializer init) { return new Cloak(init.Self, this); } + public override object Create(ActorInitializer init) { return new Cloak(this); } } - public class Cloak : UpgradableTrait, IRenderModifier, INotifyDamageStateChanged, INotifyAttack, ITick, IVisibilityModifier, IRadarColorModifier, INotifyCreated + public class Cloak : UpgradableTrait, IRenderModifier, INotifyDamageStateChanged, + INotifyAttack, ITick, IVisibilityModifier, IRadarColorModifier, INotifyCreated { [Sync] int remainingTime; [Sync] bool damageDisabled; UpgradeManager upgradeManager; - Actor self; CPos? lastPos; + bool wasCloaked = false; - public Cloak(Actor self, CloakInfo info) + public Cloak(CloakInfo info) : base(info) { - this.self = self; - remainingTime = info.InitialDelay; } - public void Created(Actor self) + void INotifyCreated.Created(Actor self) { upgradeManager = self.TraitOrDefault(); - if (remainingTime == 0) - { - if (upgradeManager != null) - foreach (var u in Info.WhileCloakedUpgrades) - upgradeManager.GrantUpgrade(self, u, this); - } - } - - protected override void UpgradeDisabled(Actor self) - { - Uncloak(); - remainingTime = Info.InitialDelay; - } - - public void Uncloak() { Uncloak(Info.CloakDelay); } - - public void Uncloak(int time) - { if (Cloaked) - { - Game.Sound.Play(Info.UncloakSound, self.CenterPosition); - if (upgradeManager != null) - foreach (var u in Info.WhileCloakedUpgrades) - upgradeManager.RevokeUpgrade(self, u, this); - } - - remainingTime = Math.Max(remainingTime, time); + GrantUpgrades(self); } - public void Attacking(Actor self, Target target, Armament a, Barrel barrel) { if (Info.UncloakOnAttack) Uncloak(); } - public bool Cloaked { get { return !IsTraitDisabled && remainingTime <= 0; } } - public void DamageStateChanged(Actor self, AttackInfo e) + public void Uncloak() { Uncloak(Info.CloakDelay); } + + public void Uncloak(int time) { remainingTime = Math.Max(remainingTime, time); } + + void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { if (Info.UncloakOn.HasFlag(UncloakType.Attack)) Uncloak(); } + + void INotifyDamageStateChanged.DamageStateChanged(Actor self, AttackInfo e) { damageDisabled = e.DamageState >= DamageState.Critical; - if (damageDisabled) + if (damageDisabled || Info.UncloakOn.HasFlag(UncloakType.Damage)) Uncloak(); } - public IEnumerable ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) + IEnumerable IRenderModifier.ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) { if (remainingTime > 0 || IsTraitDisabled) return r; @@ -124,27 +112,38 @@ namespace OpenRA.Mods.Common.Traits return SpriteRenderable.None; } - public void Tick(Actor self) + void ITick.Tick(Actor self) { - if (IsTraitDisabled) - return; - - if (remainingTime > 0 && !IsTraitDisabled && !damageDisabled && --remainingTime <= 0) + if (!IsTraitDisabled) { - Game.Sound.Play(Info.CloakSound, self.CenterPosition); - if (upgradeManager != null) - foreach (var u in Info.WhileCloakedUpgrades) - upgradeManager.GrantUpgrade(self, u, this); + if (remainingTime > 0 && !damageDisabled) + remainingTime--; + + if (self.IsDisabled()) + Uncloak(); + + if (Info.UncloakOn.HasFlag(UncloakType.Move) && (lastPos == null || lastPos.Value != self.Location)) + { + Uncloak(); + lastPos = self.Location; + } } - if (self.IsDisabled()) - Uncloak(); - - if (Info.UncloakOnMove && (lastPos == null || lastPos.Value != self.Location)) + var isCloaked = Cloaked; + if (isCloaked && !wasCloaked) { - Uncloak(); - lastPos = self.Location; + GrantUpgrades(self); + if (!self.TraitsImplementing().Any(a => a != this && a.Cloaked)) + Game.Sound.Play(Info.CloakSound, self.CenterPosition); } + else if (!isCloaked && wasCloaked) + { + RevokeUpgrades(self); + if (!self.TraitsImplementing().Any(a => a != this && a.Cloaked)) + Game.Sound.Play(Info.UncloakSound, self.CenterPosition); + } + + wasCloaked = isCloaked; } public bool IsVisible(Actor self, Player viewer) @@ -157,12 +156,26 @@ namespace OpenRA.Mods.Common.Traits && (self.CenterPosition - a.Actor.CenterPosition).LengthSquared <= a.Trait.Info.Range.LengthSquared); } - public Color RadarColorOverride(Actor self) + Color IRadarColorModifier.RadarColorOverride(Actor self) { var c = self.Owner.Color.RGB; if (self.Owner == self.World.LocalPlayer && Cloaked) c = Color.FromArgb(128, c); return c; } + + void GrantUpgrades(Actor self) + { + if (upgradeManager != null) + foreach (var u in Info.WhileCloakedUpgrades) + upgradeManager.GrantUpgrade(self, u, this); + } + + void RevokeUpgrades(Actor self) + { + if (upgradeManager != null) + foreach (var u in Info.WhileCloakedUpgrades) + upgradeManager.RevokeUpgrade(self, u, this); + } } } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index c681f7f856..25c14a3530 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -2830,6 +2830,52 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20160701 && depth == 1 && node.Key.StartsWith("Cloak")) + { + var defaultCloakType = Traits.UncloakType.Attack + | Traits.UncloakType.Unload | Traits.UncloakType.Infiltrate | Traits.UncloakType.Demolish; + + // Merge Uncloak types + var t = defaultCloakType; + for (var i = node.Value.Nodes.Count - 1; i >= 0; i--) + { + var n = node.Value.Nodes[i]; + var v = string.Compare(n.Value.Value, "true", true) == 0; + Traits.UncloakType flag; + if (n.Key == "UncloakOnAttack") + flag = Traits.UncloakType.Attack; + else if (n.Key == "UncloakOnMove") + flag = Traits.UncloakType.Move; + else if (n.Key == "UncloakOnUnload") + flag = Traits.UncloakType.Unload; + else if (n.Key == "UncloakOnInfiltrate") + flag = Traits.UncloakType.Infiltrate; + else if (n.Key == "UncloakOnDemolish") + flag = Traits.UncloakType.Demolish; + else + continue; + t = v ? t | flag : t & ~flag; + node.Value.Nodes.Remove(n); + } + + if (t != defaultCloakType) + { + Console.WriteLine("\t\tCloak type: " + t.ToString()); + var ts = new List(); + if (t.HasFlag(Traits.UncloakType.Attack)) + ts.Add("Attack"); + if (t.HasFlag(Traits.UncloakType.Unload)) + ts.Add("Unload"); + if (t.HasFlag(Traits.UncloakType.Infiltrate)) + ts.Add("Infiltrate"); + if (t.HasFlag(Traits.UncloakType.Demolish)) + ts.Add("Demolish"); + if (t.HasFlag(Traits.UncloakType.Move)) + ts.Add("Move"); + node.Value.Nodes.Add(new MiniYamlNode("UncloakOn", ts.JoinWith(", "))); + } + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/OpenRA.Mods.RA/Activities/Infiltrate.cs b/OpenRA.Mods.RA/Activities/Infiltrate.cs index ff08174378..9ac22715d0 100644 --- a/OpenRA.Mods.RA/Activities/Infiltrate.cs +++ b/OpenRA.Mods.RA/Activities/Infiltrate.cs @@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA.Activities if (!validStances.HasStance(stance)) return; - if (cloak != null && cloak.Info.UncloakOnInfiltrate) + if (cloak != null && cloak.Info.UncloakOn.HasFlag(UncloakType.Infiltrate)) cloak.Uncloak(); foreach (var t in target.TraitsImplementing()) diff --git a/mods/d2k/rules/infantry.yaml b/mods/d2k/rules/infantry.yaml index 83bcbf3217..dc9f4fe5f9 100644 --- a/mods/d2k/rules/infantry.yaml +++ b/mods/d2k/rules/infantry.yaml @@ -251,7 +251,7 @@ saboteur: CloakDelay: 85 CloakSound: STEALTH1.WAV UncloakSound: STEALTH2.WAV - UncloakOnMove: true + UncloakOn: Attack, Unload, Infiltrate, Demolish, Move IsPlayerPalette: true Voiced: VoiceSet: SaboteurVoice diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index dd223af15a..92a9cdb7a7 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -458,7 +458,7 @@ HIJACKER: Cloak: InitialDelay: 250 CloakDelay: 120 - UncloakOnMove: true + UncloakOn: Attack, Unload, Infiltrate, Demolish, Move CloakTypes: Cloak, Hijacker IsPlayerPalette: true Mobile: @@ -538,7 +538,7 @@ SNIPER: CloakDelay: 120 CloakSound: UncloakSound: - UncloakOnMove: true + UncloakOn: Attack, Unload, Infiltrate, Demolish, Move IsPlayerPalette: true DetectCloaked: CloakTypes: Cloak, Hijacker