From e34380381047a613ba4de7f4680ca76e622623a9 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Tue, 29 Dec 2015 14:50:47 +0100 Subject: [PATCH 1/9] Remove an unnecessary Where in Crate.cs --- OpenRA.Mods.Common/Traits/Crates/Crate.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index 58c62d43cb..220d025014 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -178,8 +178,8 @@ namespace OpenRA.Mods.Common.Traits return SubCell.FullCell; return !self.World.ActorMap.GetActorsAt(cell) - .Where(x => x != ignoreActor) - .Any() ? SubCell.FullCell : SubCell.Invalid; + .Any(x => x != ignoreActor) + ? SubCell.FullCell : SubCell.Invalid; } public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true) From 8b59ce4dc22aa45f3f71c1c0a7f1dc92b2657294 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Fri, 22 Jan 2016 10:56:04 +0100 Subject: [PATCH 2/9] Add a new INotifyCrushed interface --- OpenRA.Game/Traits/TraitsInterfaces.cs | 9 +++- OpenRA.Mods.Common/Traits/Crates/Crate.cs | 65 +++++++++++++---------- OpenRA.Mods.Common/Traits/Crushable.cs | 13 +++-- OpenRA.Mods.Common/Traits/Mobile.cs | 16 +++--- OpenRA.Mods.RA/Traits/Mine.cs | 15 +++--- 5 files changed, 70 insertions(+), 48 deletions(-) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index d9412b941e..6a50dc87c3 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -303,11 +303,16 @@ namespace OpenRA.Traits public interface ICrushable { - void OnCrush(Actor crusher); - void WarnCrush(Actor crusher); bool CrushableBy(HashSet crushClasses, Player owner); } + [RequireExplicitImplementation] + public interface INotifyCrushed + { + void OnCrush(Actor self, Actor crusher, HashSet crushClasses); + void WarnCrush(Actor self, Actor crusher, HashSet crushClasses); + } + public interface ITraitInfoInterface { } public interface ITraitInfo : ITraitInfoInterface { object Create(ActorInitializer init); } diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index 220d025014..213d7581de 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -38,7 +38,8 @@ namespace OpenRA.Mods.Common.Traits bool IOccupySpaceInfo.SharesCell { get { return false; } } } - class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld + class Crate : ITick, IPositionable, ICrushable, ISync, + INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyCrushed { readonly Actor self; readonly CrateInfo info; @@ -56,36 +57,14 @@ namespace OpenRA.Mods.Common.Traits SetPosition(self, init.Get()); } - public void WarnCrush(Actor crusher) { } + void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { } - public void OnCrush(Actor crusher) + void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { - if (collected) + if (!CrushableBy(crushClasses, crusher.Owner)) return; - var crateActions = self.TraitsImplementing(); - - self.Dispose(); - collected = true; - - if (crateActions.Any()) - { - var shares = crateActions.Select(a => Pair.New(a, a.GetSelectionSharesOuter(crusher))); - - var totalShares = shares.Sum(a => a.Second); - var n = self.World.SharedRandom.Next(totalShares); - - foreach (var s in shares) - { - if (n < s.Second) - { - s.First.Activate(crusher); - return; - } - else - n -= s.Second; - } - } + OnCrushInner(crusher); } public void OnLanded() @@ -110,11 +89,41 @@ namespace OpenRA.Mods.Common.Traits // Destroy the crate if none of the units in the cell are valid collectors if (collector != null) - OnCrush(collector); + OnCrushInner(collector); else self.Dispose(); } + void OnCrushInner(Actor crusher) + { + if (collected) + return; + + var crateActions = self.TraitsImplementing(); + + self.Dispose(); + collected = true; + + if (crateActions.Any()) + { + var shares = crateActions.Select(a => Pair.New(a, a.GetSelectionSharesOuter(crusher))); + + var totalShares = shares.Sum(a => a.Second); + var n = self.World.SharedRandom.Next(totalShares); + + foreach (var s in shares) + { + if (n < s.Second) + { + s.First.Activate(crusher); + return; + } + + n -= s.Second; + } + } + } + public void Tick(Actor self) { if (info.Lifetime != 0 && self.IsInWorld && ++ticks >= info.Lifetime * 25) diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index f429ca82f1..6d4f4e0ee7 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -29,7 +29,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new Crushable(init.Self, this); } } - class Crushable : ICrushable + class Crushable : ICrushable, INotifyCrushed { readonly Actor self; readonly CrushableInfo info; @@ -40,16 +40,23 @@ namespace OpenRA.Mods.Common.Traits this.info = info; } - public void WarnCrush(Actor crusher) + void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { + if (!CrushableBy(crushClasses, crusher.Owner)) + return; + var mobile = self.TraitOrDefault(); if (mobile != null && self.World.SharedRandom.Next(100) <= info.WarnProbability) mobile.Nudge(self, crusher, true); } - public void OnCrush(Actor crusher) + void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { + if (!CrushableBy(crushClasses, crusher.Owner)) + return; + Game.Sound.Play(info.CrushSound, crusher.CenterPosition); + var wda = self.TraitsImplementing() .FirstOrDefault(s => s.Info.CrushedSequence != null); if (wda != null) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 46df88d34c..304109a2c2 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -594,10 +594,10 @@ namespace OpenRA.Mods.Common.Traits if (!self.IsAtGroundLevel()) return; - var crushables = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Where(b => b.CrushableBy(Info.Crushes, self.Owner))); - foreach (var crushable in crushables) - crushable.WarnCrush(self); + var notifiers = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) + .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + foreach (var notifyCrushed in notifiers) + notifyCrushed.Trait.WarnCrush(notifyCrushed.Actor, self, Info.Crushes); } public void FinishedMoving(Actor self) @@ -606,10 +606,10 @@ namespace OpenRA.Mods.Common.Traits if (!self.IsAtGroundLevel()) return; - var crushables = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Where(c => c.CrushableBy(Info.Crushes, self.Owner))); - foreach (var crushable in crushables) - crushable.OnCrush(self); + var notifiers = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) + .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + foreach (var notifyCrushed in notifiers) + notifyCrushed.Trait.OnCrush(notifyCrushed.Actor, self, Info.Crushes); } public int MovementSpeedForCell(Actor self, CPos cell) diff --git a/OpenRA.Mods.RA/Traits/Mine.cs b/OpenRA.Mods.RA/Traits/Mine.cs index caed682f1b..a291c4fb1a 100644 --- a/OpenRA.Mods.RA/Traits/Mine.cs +++ b/OpenRA.Mods.RA/Traits/Mine.cs @@ -20,24 +20,25 @@ namespace OpenRA.Mods.RA.Traits public readonly bool AvoidFriendly = true; public readonly HashSet DetonateClasses = new HashSet(); - public object Create(ActorInitializer init) { return new Mine(init, this); } + public object Create(ActorInitializer init) { return new Mine(this); } } - class Mine : ICrushable + class Mine : ICrushable, INotifyCrushed { - readonly Actor self; readonly MineInfo info; - public Mine(ActorInitializer init, MineInfo info) + public Mine(MineInfo info) { - self = init.Self; this.info = info; } - public void WarnCrush(Actor crusher) { } + void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { } - public void OnCrush(Actor crusher) + void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { + if (!CrushableBy(crushClasses, crusher.Owner)) + return; + if (crusher.Info.HasTraitInfo() || (self.Owner.Stances[crusher.Owner] == Stance.Ally && info.AvoidFriendly)) return; From f21d1f52e73c259bd5375437c84825dfd9f7c8b9 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Fri, 22 Jan 2016 11:01:26 +0100 Subject: [PATCH 3/9] Make the ICrushable implementation explicit --- OpenRA.Mods.Common/Traits/Crates/Crate.cs | 8 +++++--- OpenRA.Mods.Common/Traits/Crushable.cs | 11 ++++++++--- OpenRA.Mods.RA/Traits/Mine.cs | 4 ++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index 213d7581de..ee09ad83e0 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -61,7 +61,8 @@ namespace OpenRA.Mods.Common.Traits void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { - if (!CrushableBy(crushClasses, crusher.Owner)) + // Crate can only be crushed if it is not in the air. + if (!self.IsAtGroundLevel() || !crushClasses.Contains(info.CrushClass)) return; OnCrushInner(crusher); @@ -84,7 +85,8 @@ namespace OpenRA.Mods.Common.Traits return false; // Make sure that the actor can collect this crate type - return CrushableBy(mi.Crushes, a.Owner); + // Crate can only be crushed if it is not in the air. + return self.IsAtGroundLevel() && mi.Crushes.Contains(info.CrushClass); }); // Destroy the crate if none of the units in the cell are valid collectors @@ -196,7 +198,7 @@ namespace OpenRA.Mods.Common.Traits return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid; } - public bool CrushableBy(HashSet crushClasses, Player owner) + bool ICrushable.CrushableBy(HashSet crushClasses, Player owner) { // Crate can only be crushed if it is not in the air. return self.IsAtGroundLevel() && crushClasses.Contains(info.CrushClass); diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 6d4f4e0ee7..16f8e5466c 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { - if (!CrushableBy(crushClasses, crusher.Owner)) + if (!CrushableInner(crushClasses, crusher.Owner)) return; var mobile = self.TraitOrDefault(); @@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { - if (!CrushableBy(crushClasses, crusher.Owner)) + if (!CrushableInner(crushClasses, crusher.Owner)) return; Game.Sound.Play(info.CrushSound, crusher.CenterPosition); @@ -71,7 +71,12 @@ namespace OpenRA.Mods.Common.Traits self.Kill(crusher); } - public bool CrushableBy(HashSet crushClasses, Player crushOwner) + bool ICrushable.CrushableBy(HashSet crushClasses, Player crushOwner) + { + return CrushableInner(crushClasses, crushOwner); + } + + bool CrushableInner(HashSet crushClasses, Player crushOwner) { // Only make actor crushable if it is on the ground. if (!self.IsAtGroundLevel()) diff --git a/OpenRA.Mods.RA/Traits/Mine.cs b/OpenRA.Mods.RA/Traits/Mine.cs index a291c4fb1a..089ffb20dc 100644 --- a/OpenRA.Mods.RA/Traits/Mine.cs +++ b/OpenRA.Mods.RA/Traits/Mine.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Traits void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { - if (!CrushableBy(crushClasses, crusher.Owner)) + if (!info.CrushClasses.Overlaps(crushClasses)) return; if (crusher.Info.HasTraitInfo() || (self.Owner.Stances[crusher.Owner] == Stance.Ally && info.AvoidFriendly)) @@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA.Traits self.Kill(crusher); } - public bool CrushableBy(HashSet crushClasses, Player owner) + bool ICrushable.CrushableBy(HashSet crushClasses, Player owner) { return info.CrushClasses.Overlaps(crushClasses); } From 17e23a7adc0669852105708c4625683792ecc025 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Fri, 22 Jan 2016 11:02:38 +0100 Subject: [PATCH 4/9] Add support for a FallbackSequence in WithDeathAnimation Used when the actor is killed by non-standard means, like suicide. --- .../Traits/Render/WithDeathAnimation.cs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs index de39214c40..9298f1bd85 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs @@ -45,6 +45,9 @@ namespace OpenRA.Mods.Common.Traits "Is only used if UseDeathTypeSuffix is `True`.")] public readonly Dictionary DeathTypes = new Dictionary(); + [Desc("Sequence to use when the actor is killed by some non-standard means (e.g. suicide).")] + [SequenceReference] public readonly string FallbackSequence = null; + public static object LoadDeathTypes(MiniYaml yaml) { var md = yaml.ToDictionary(); @@ -57,10 +60,11 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new WithDeathAnimation(init.Self, this); } } - public class WithDeathAnimation : INotifyKilled + public class WithDeathAnimation : INotifyKilled, INotifyCrushed { public readonly WithDeathAnimationInfo Info; readonly RenderSprites rs; + bool crushed; public WithDeathAnimation(Actor self, WithDeathAnimationInfo info) { @@ -70,11 +74,23 @@ namespace OpenRA.Mods.Common.Traits public void Killed(Actor self, AttackInfo e) { - // Killed by some non-standard means. This includes being crushed - // by a vehicle (Actors with Crushable trait will spawn CrushedSequence instead). - if (e.Warhead == null || !(e.Warhead is DamageWarhead)) + // Actors with Crushable trait will spawn CrushedSequence. + if (crushed) return; + var palette = Info.DeathSequencePalette; + if (Info.DeathPaletteIsPlayerPalette) + palette += self.Owner.InternalName; + + // Killed by some non-standard means + if (e.Warhead == null || !(e.Warhead is DamageWarhead)) + { + if (Info.FallbackSequence != null) + SpawnDeathAnimation(self, Info.FallbackSequence, palette); + + return; + } + var sequence = Info.DeathSequence; if (Info.UseDeathTypeSuffix) { @@ -86,10 +102,6 @@ namespace OpenRA.Mods.Common.Traits sequence += Info.DeathTypes[damageType]; } - var palette = Info.DeathSequencePalette; - if (Info.DeathPaletteIsPlayerPalette) - palette += self.Owner.InternalName; - SpawnDeathAnimation(self, sequence, palette); } @@ -101,5 +113,12 @@ namespace OpenRA.Mods.Common.Traits w.Add(new Corpse(w, self.CenterPosition, rs.GetImage(self), sequence, palette)); }); } + + void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) + { + crushed = true; + } + + void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { } } } From 9ea2a7a0bd6409f1c4df3bcf21a3aa39d93af7c9 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Sat, 23 Jan 2016 14:38:04 +0100 Subject: [PATCH 5/9] Add [RequireExplicitImplementation] to the ICrushable interface --- OpenRA.Game/Traits/TraitsInterfaces.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 6a50dc87c3..50187777f5 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -301,6 +301,7 @@ namespace OpenRA.Traits public interface IFacingInfo : ITraitInfoInterface { int GetInitialFacing(); } + [RequireExplicitImplementation] public interface ICrushable { bool CrushableBy(HashSet crushClasses, Player owner); From fe5754e2bd9eb57584a80acefcaf851724df04c7 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Sat, 23 Jan 2016 15:39:03 +0100 Subject: [PATCH 6/9] Change the parameters of ICrushable.CrushableBy --- OpenRA.Game/Traits/TraitsInterfaces.cs | 2 +- OpenRA.Mods.Common/Traits/Crates/Crate.cs | 2 +- OpenRA.Mods.Common/Traits/Crushable.cs | 4 ++-- OpenRA.Mods.Common/Traits/Mobile.cs | 2 +- OpenRA.Mods.RA/Traits/Mine.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 50187777f5..f6195b08f2 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -304,7 +304,7 @@ namespace OpenRA.Traits [RequireExplicitImplementation] public interface ICrushable { - bool CrushableBy(HashSet crushClasses, Player owner); + bool CrushableBy(Actor self, Actor crusher, HashSet crushClasses); } [RequireExplicitImplementation] diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index ee09ad83e0..6b175539e0 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -198,7 +198,7 @@ namespace OpenRA.Mods.Common.Traits return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid; } - bool ICrushable.CrushableBy(HashSet crushClasses, Player owner) + bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) { // Crate can only be crushed if it is not in the air. return self.IsAtGroundLevel() && crushClasses.Contains(info.CrushClass); diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 16f8e5466c..48fe759911 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -71,9 +71,9 @@ namespace OpenRA.Mods.Common.Traits self.Kill(crusher); } - bool ICrushable.CrushableBy(HashSet crushClasses, Player crushOwner) + bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) { - return CrushableInner(crushClasses, crushOwner); + return CrushableInner(crushClasses, crusher.Owner); } bool CrushableInner(HashSet crushClasses, Player crushOwner) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 304109a2c2..806f82f2db 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -260,7 +260,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var crushable in crushables) { lacksCrushability = false; - if (!crushable.CrushableBy(Crushes, self.Owner)) + if (!crushable.CrushableBy(otherActor, self, Crushes)) return true; } diff --git a/OpenRA.Mods.RA/Traits/Mine.cs b/OpenRA.Mods.RA/Traits/Mine.cs index 089ffb20dc..560492e69f 100644 --- a/OpenRA.Mods.RA/Traits/Mine.cs +++ b/OpenRA.Mods.RA/Traits/Mine.cs @@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA.Traits self.Kill(crusher); } - bool ICrushable.CrushableBy(HashSet crushClasses, Player owner) + bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) { return info.CrushClasses.Overlaps(crushClasses); } From a46815e53296fbba8172ca24ca6b7f4bcbe4153f Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Fri, 22 Jan 2016 13:55:05 +0100 Subject: [PATCH 7/9] Move the creation of the CrushedSequence to WithDeathAnimation --- OpenRA.Mods.Common/Traits/Crushable.cs | 11 ----------- .../Traits/Render/WithDeathAnimation.cs | 9 +++++++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 48fe759911..5a4dd71c7b 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -57,17 +57,6 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.Play(info.CrushSound, crusher.CenterPosition); - var wda = self.TraitsImplementing() - .FirstOrDefault(s => s.Info.CrushedSequence != null); - if (wda != null) - { - var palette = wda.Info.CrushedSequencePalette; - if (wda.Info.CrushedPaletteIsPlayerPalette) - palette += self.Owner.InternalName; - - wda.SpawnDeathAnimation(self, wda.Info.CrushedSequence, palette); - } - self.Kill(crusher); } diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs index 9298f1bd85..50333feb47 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs @@ -117,6 +117,15 @@ namespace OpenRA.Mods.Common.Traits void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) { crushed = true; + + if (Info.CrushedSequence == null) + return; + + var crushPalette = Info.CrushedSequencePalette; + if (Info.CrushedPaletteIsPlayerPalette) + crushPalette += self.Owner.InternalName; + + SpawnDeathAnimation(self, Info.CrushedSequence, crushPalette); } void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { } From 24ec7b0ad7958ea357194a4cc82e950d4048f07e Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Fri, 29 Jan 2016 21:40:15 +0100 Subject: [PATCH 8/9] Fix the mobile trait spamming crush-notifications --- OpenRA.Mods.Common/Traits/Mobile.cs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 806f82f2db..11c6aeacf5 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -594,8 +594,11 @@ namespace OpenRA.Mods.Common.Traits if (!self.IsAtGroundLevel()) return; - var notifiers = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + var actors = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self).ToList(); + if (!AnyCrushables(actors)) + return; + + var notifiers = actors.SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); foreach (var notifyCrushed in notifiers) notifyCrushed.Trait.WarnCrush(notifyCrushed.Actor, self, Info.Crushes); } @@ -606,12 +609,28 @@ namespace OpenRA.Mods.Common.Traits if (!self.IsAtGroundLevel()) return; - var notifiers = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + var actors = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self).ToList(); + if (!AnyCrushables(actors)) + return; + + var notifiers = actors.SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); foreach (var notifyCrushed in notifiers) notifyCrushed.Trait.OnCrush(notifyCrushed.Actor, self, Info.Crushes); } + bool AnyCrushables(List actors) + { + var crushables = actors.SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))).ToList(); + if (crushables.Count == 0) + return false; + + foreach (var crushes in crushables) + if (!crushes.Trait.CrushableBy(crushes.Actor, self, Info.Crushes)) + return false; + + return true; + } + public int MovementSpeedForCell(Actor self, CPos cell) { var index = self.World.Map.GetTerrainIndex(cell); From 5327ea1418afadd8cb862731f62ba272f202ab7f Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Fri, 29 Jan 2016 21:40:44 +0100 Subject: [PATCH 9/9] Fix crushed sequences not appearing due to an FrameEndTask --- .../Traits/Render/WithDeathAnimation.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs index 50333feb47..51239d295a 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs @@ -86,7 +86,7 @@ namespace OpenRA.Mods.Common.Traits if (e.Warhead == null || !(e.Warhead is DamageWarhead)) { if (Info.FallbackSequence != null) - SpawnDeathAnimation(self, Info.FallbackSequence, palette); + SpawnDeathAnimation(self, self.CenterPosition, rs.GetImage(self), Info.FallbackSequence, palette); return; } @@ -102,16 +102,12 @@ namespace OpenRA.Mods.Common.Traits sequence += Info.DeathTypes[damageType]; } - SpawnDeathAnimation(self, sequence, palette); + SpawnDeathAnimation(self, self.CenterPosition, rs.GetImage(self), sequence, palette); } - public void SpawnDeathAnimation(Actor self, string sequence, string palette) + public void SpawnDeathAnimation(Actor self, WPos pos, string image, string sequence, string palette) { - self.World.AddFrameEndTask(w => - { - if (!self.Disposed) - w.Add(new Corpse(w, self.CenterPosition, rs.GetImage(self), sequence, palette)); - }); + self.World.AddFrameEndTask(w => w.Add(new Corpse(w, pos, image, sequence, palette))); } void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClasses) @@ -125,7 +121,7 @@ namespace OpenRA.Mods.Common.Traits if (Info.CrushedPaletteIsPlayerPalette) crushPalette += self.Owner.InternalName; - SpawnDeathAnimation(self, Info.CrushedSequence, crushPalette); + SpawnDeathAnimation(self, self.CenterPosition, rs.GetImage(self), Info.CrushedSequence, crushPalette); } void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet crushClasses) { }