diff --git a/OpenRA.Game/GameRules/DamageWarhead.cs b/OpenRA.Game/GameRules/DamageWarhead.cs index 42c8094a82..f76171bee1 100644 --- a/OpenRA.Game/GameRules/DamageWarhead.cs +++ b/OpenRA.Game/GameRules/DamageWarhead.cs @@ -8,10 +8,7 @@ */ #endregion -using System; using System.Collections.Generic; -using System.Linq; -using OpenRA.Effects; using OpenRA.Traits; namespace OpenRA.GameRules @@ -24,9 +21,6 @@ namespace OpenRA.GameRules [Desc("Types of damage that this warhead causes. Leave empty for no damage.")] public readonly string[] DamageTypes = new string[0]; - [Desc("Infantry death animation to use.")] - public readonly string DeathType = "1"; - [FieldLoader.LoadUsing("LoadVersus")] [Desc("Damage percentage versus each armortype. 0% = can't target.")] public readonly Dictionary Versus; diff --git a/OpenRA.Mods.Cnc/Traits/SpawnViceroid.cs b/OpenRA.Mods.Cnc/Traits/SpawnViceroid.cs index ab4b24a364..1cf6642d38 100644 --- a/OpenRA.Mods.Cnc/Traits/SpawnViceroid.cs +++ b/OpenRA.Mods.Cnc/Traits/SpawnViceroid.cs @@ -19,36 +19,36 @@ namespace OpenRA.Mods.Cnc.Traits [ActorReference] public readonly string ViceroidActor = "vice"; public readonly int Probability = 10; public readonly string Owner = "Creeps"; - public readonly string DeathType = "6"; + public readonly string DeathType = "TiberiumDeath"; public object Create(ActorInitializer init) { return new SpawnViceroid(this); } } class SpawnViceroid : INotifyKilled { - readonly SpawnViceroidInfo spawnViceroidInfo; + readonly SpawnViceroidInfo info; - public SpawnViceroid(SpawnViceroidInfo info) { spawnViceroidInfo = info; } + public SpawnViceroid(SpawnViceroidInfo info) { this.info = info; } public void Killed(Actor self, AttackInfo e) { if (!self.World.LobbyInfo.GlobalSettings.Creeps) return; - if (e.Warhead == null || e.Warhead.DeathType != spawnViceroidInfo.DeathType) return; - if (self.World.SharedRandom.Next(100) > spawnViceroidInfo.Probability) return; + if (e.Warhead == null || !e.Warhead.DamageTypes.Contains(info.DeathType)) return; + if (self.World.SharedRandom.Next(100) > info.Probability) return; self.World.AddFrameEndTask(w => { var td = new TypeDictionary { new LocationInit(self.Location), - new OwnerInit(self.World.Players.First(p => p.InternalName == spawnViceroidInfo.Owner)) + new OwnerInit(self.World.Players.First(p => p.InternalName == info.Owner)) }; var facing = self.TraitOrDefault(); if (facing != null) td.Add(new FacingInit(facing.Facing)); - w.CreateActor(spawnViceroidInfo.ViceroidActor, td); + w.CreateActor(info.ViceroidActor, td); }); } } diff --git a/OpenRA.Mods.Common/Traits/Explodes.cs b/OpenRA.Mods.Common/Traits/Explodes.cs index 2703339b87..58bd5f05b3 100644 --- a/OpenRA.Mods.Common/Traits/Explodes.cs +++ b/OpenRA.Mods.Common/Traits/Explodes.cs @@ -9,7 +9,6 @@ #endregion using System.Linq; -using OpenRA.GameRules; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -29,37 +28,37 @@ namespace OpenRA.Mods.Common.Traits public class Explodes : INotifyKilled { - readonly ExplodesInfo explodesInfo; + readonly ExplodesInfo info; - public Explodes(ExplodesInfo info) { explodesInfo = info; } + public Explodes(ExplodesInfo info) { this.info = info; } public void Killed(Actor self, AttackInfo e) { if (!self.IsInWorld) return; - if (self.World.SharedRandom.Next(100) > explodesInfo.Chance) + if (self.World.SharedRandom.Next(100) > info.Chance) return; - if (explodesInfo.DeathType != null && e.Warhead != null && !explodesInfo.DeathType.Contains(e.Warhead.DeathType)) + if (info.DeathType != null && e.Warhead != null && !info.DeathType.Intersect(e.Warhead.DamageTypes).Any()) return; var weaponName = ChooseWeaponForExplosion(self); - if (weaponName != null) - { - var weapon = e.Attacker.World.Map.Rules.Weapons[weaponName.ToLowerInvariant()]; - if (weapon.Report != null && weapon.Report.Any()) - Sound.Play(weapon.Report.Random(e.Attacker.World.SharedRandom), self.CenterPosition); + if (weaponName == null) + return; - // Use .FromPos since this actor is killed. Cannot use Target.FromActor - weapon.Impact(Target.FromPos(self.CenterPosition), e.Attacker, Enumerable.Empty()); - } + var weapon = e.Attacker.World.Map.Rules.Weapons[weaponName.ToLowerInvariant()]; + if (weapon.Report != null && weapon.Report.Any()) + Sound.Play(weapon.Report.Random(e.Attacker.World.SharedRandom), self.CenterPosition); + + // Use .FromPos since this actor is killed. Cannot use Target.FromActor + weapon.Impact(Target.FromPos(self.CenterPosition), e.Attacker, Enumerable.Empty()); } string ChooseWeaponForExplosion(Actor self) { var shouldExplode = self.TraitsImplementing().All(a => a.ShouldExplode(self)); - return shouldExplode ? explodesInfo.Weapon : explodesInfo.EmptyWeapon; + return shouldExplode ? info.Weapon : info.EmptyWeapon; } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs index 90388b286a..58d8142a10 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs @@ -8,6 +8,9 @@ */ #endregion +using System; +using System.Collections.Generic; +using System.Linq; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -18,17 +21,39 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Sequence to play when this actor is killed by a warhead.")] public readonly string DeathSequence = "die"; + + [Desc("The palette used for `DeathSequence`.")] public readonly string DeathSequencePalette = "player"; + [Desc("Custom death animation palette is a player palette BaseName")] public readonly bool DeathPaletteIsPlayerPalette = true; + [Desc("Should DeathType-specific sequences be used (sequence name = DeathSequence + DeathType).")] public readonly bool UseDeathTypeSuffix = true; + [Desc("Sequence to play when this actor is crushed.")] public readonly string CrushedSequence = "die-crushed"; + + [Desc("The palette used for `CrushedSequence`.")] public readonly string CrushedSequencePalette = "effect"; + [Desc("Custom crushed animation palette is a player palette BaseName")] public readonly bool CrushedPaletteIsPlayerPalette = false; + [FieldLoader.LoadUsing("LoadDeathTypes")] + [Desc("Death animation to use for each damage type (defined on the warheads).", + "Is only used if UseDeathTypeSuffix is `True`.")] + public readonly Dictionary DeathTypes = new Dictionary(); + + public static object LoadDeathTypes(MiniYaml yaml) + { + var md = yaml.ToDictionary(); + + return md.ContainsKey("DeathTypes") + ? md["DeathTypes"].ToDictionary(my => FieldLoader.GetValue("(value)", my.Value)) + : new Dictionary(); + } + public object Create(ActorInitializer init) { return new WithDeathAnimation(init.Self, this); } } @@ -52,7 +77,14 @@ namespace OpenRA.Mods.Common.Traits var sequence = Info.DeathSequence; if (Info.UseDeathTypeSuffix) - sequence += e.Warhead.DeathType; + { + var damageType = e.Warhead.DamageTypes.Intersect(Info.DeathTypes.Keys).FirstOrDefault(); + if (damageType == null) + throw new Exception("Actor type `{0}` does not define a death animation for weapon with damage types `{1}`!" + .F(self.Info.Name, string.Join(", ", e.Warhead.DamageTypes))); + + sequence += Info.DeathTypes[damageType]; + } var palette = Info.DeathSequencePalette; if (Info.DeathPaletteIsPlayerPalette) diff --git a/OpenRA.Mods.Common/Traits/Sound/DeathSounds.cs b/OpenRA.Mods.Common/Traits/Sound/DeathSounds.cs index 1f590806ed..30671a9133 100644 --- a/OpenRA.Mods.Common/Traits/Sound/DeathSounds.cs +++ b/OpenRA.Mods.Common/Traits/Sound/DeathSounds.cs @@ -22,7 +22,8 @@ namespace OpenRA.Mods.Common.Traits [Desc("Multiply volume with this factor.")] public readonly float VolumeMultiplier = 1f; - [Desc("DeathTypes that this should be used for. If empty, this will be used as the default sound.")] + [Desc("Damage types that this should be used for (defined on the warheads).", + "If empty, this will be used as the default sound for all death types.")] public readonly string[] DeathTypes = { }; public object Create(ActorInitializer init) { return new DeathSounds(this); } @@ -40,8 +41,7 @@ namespace OpenRA.Mods.Common.Traits if (e.Warhead == null) return; - if (info.DeathTypes.Contains(e.Warhead.DeathType) || (!info.DeathTypes.Any() && - !self.Info.Traits.WithInterface().Any(dsi => dsi.DeathTypes.Contains(e.Warhead.DeathType)))) + if (info.DeathTypes.Intersect(e.Warhead.DamageTypes).Any()) self.PlayVoiceLocal(info.DeathSound, info.VolumeMultiplier); } } diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index d4cdf27675..8d0a47efe9 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -225,6 +225,7 @@ Huntable: ScriptTriggers: DeathSounds: + DeathTypes: 1, 2, 3, 4 Parachutable: FallRate: 130 GainsStatUpgrades: