diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 7d25eac80f..b0a8d64414 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -321,12 +321,12 @@ namespace OpenRA health.InflictDamage(this, attacker, damage, false); } - public void Kill(Actor attacker) + public void Kill(Actor attacker, HashSet damageTypes = null) { if (Disposed || health == null) return; - health.Kill(this, attacker); + health.Kill(this, attacker, damageTypes); } public bool CanBeViewedByPlayer(Player player) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 9b2eee1f79..f46fb5b001 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -42,7 +42,7 @@ namespace OpenRA.Traits bool IsDead { get; } void InflictDamage(Actor self, Actor attacker, Damage damage, bool ignoreModifiers); - void Kill(Actor self, Actor attacker); + void Kill(Actor self, Actor attacker, HashSet damageTypes); } // depends on the order of pips in WorldRenderer.cs! diff --git a/OpenRA.Mods.Cnc/Activities/Leap.cs b/OpenRA.Mods.Cnc/Activities/Leap.cs index 1d7f11e46d..999cabeff7 100644 --- a/OpenRA.Mods.Cnc/Activities/Leap.cs +++ b/OpenRA.Mods.Cnc/Activities/Leap.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using OpenRA.Activities; using OpenRA.GameRules; @@ -29,8 +30,9 @@ namespace OpenRA.Mods.Cnc.Activities WPos to; int ticks; WAngle angle; + HashSet damageTypes; - public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle) + public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle, HashSet damageTypes) { var targetMobile = target.TraitOrDefault(); if (targetMobile == null) @@ -38,6 +40,7 @@ namespace OpenRA.Mods.Cnc.Activities this.weapon = a.Weapon; this.angle = angle; + this.damageTypes = damageTypes; mobile = self.Trait(); mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, targetMobile.FromCell, targetMobile.FromSubCell); mobile.IsMoving = true; @@ -67,7 +70,7 @@ namespace OpenRA.Mods.Cnc.Activities self.World.ActorMap.GetActorsAt(mobile.ToCell, mobile.ToSubCell) .Except(new[] { self }).Where(t => weapon.IsValidAgainst(t, self)) - .Do(t => t.Kill(self)); + .Do(t => t.Kill(self, damageTypes)); return NextActivity; } diff --git a/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs b/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs index a3b1baf0c2..0d86bef57d 100644 --- a/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs +++ b/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs @@ -25,6 +25,9 @@ namespace OpenRA.Mods.Cnc.Traits public readonly WAngle Angle = WAngle.FromDegrees(20); + [Desc("Types of damage that this trait causes. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public override object Create(ActorInitializer init) { return new AttackLeap(init.Self, this); } } @@ -51,7 +54,7 @@ namespace OpenRA.Mods.Cnc.Traits return; self.CancelActivity(); - self.QueueActivity(new Leap(self, target.Actor, a, info.Speed, info.Angle)); + self.QueueActivity(new Leap(self, target.Actor, a, info.Speed, info.Angle, info.DamageTypes)); } } } diff --git a/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs b/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs index ba5415c66f..d613416827 100644 --- a/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs +++ b/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using System.Drawing; using OpenRA.Mods.Cnc.Activities; using OpenRA.Mods.Common.Traits; @@ -22,6 +23,10 @@ namespace OpenRA.Mods.Cnc.Traits { [Desc("Should the actor die instead of being teleported?")] public readonly bool ExplodeInstead = false; + + [Desc("Types of damage that this trait causes to self when 'ExplodeInstead' is true. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public readonly string ChronoshiftSound = "chrono2.aud"; [Desc("Should the actor return to its previous location after the chronoshift wore out?")] @@ -95,7 +100,7 @@ namespace OpenRA.Mods.Cnc.Traits { // Damage is inflicted by the chronosphere if (!self.Disposed) - self.InflictDamage(chronosphere, new Damage(int.MaxValue)); + self.Kill(chronosphere, info.DamageTypes); }); return true; } diff --git a/OpenRA.Mods.Cnc/Traits/MadTank.cs b/OpenRA.Mods.Cnc/Traits/MadTank.cs index ebcaa38e66..8b534deb2e 100644 --- a/OpenRA.Mods.Cnc/Traits/MadTank.cs +++ b/OpenRA.Mods.Cnc/Traits/MadTank.cs @@ -53,6 +53,9 @@ namespace OpenRA.Mods.Cnc.Traits public WeaponInfo ThumpDamageWeaponInfo { get; private set; } public WeaponInfo DetonationWeaponInfo { get; private set; } + [Desc("Types of damage that this trait causes to self while self-destructing. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public object Create(ActorInitializer init) { return new MadTank(init.Self, this); } public void RulesetLoaded(Ruleset rules, ActorInfo ai) { @@ -146,7 +149,7 @@ namespace OpenRA.Mods.Cnc.Traits info.DetonationWeaponInfo.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); } - self.Kill(self); + self.Kill(self, info.DamageTypes); }); } diff --git a/OpenRA.Mods.Cnc/Traits/Mine.cs b/OpenRA.Mods.Cnc/Traits/Mine.cs index 8d95b7d0b3..07dd13761e 100644 --- a/OpenRA.Mods.Cnc/Traits/Mine.cs +++ b/OpenRA.Mods.Cnc/Traits/Mine.cs @@ -48,7 +48,7 @@ namespace OpenRA.Mods.Cnc.Traits if (mobile != null && !info.DetonateClasses.Overlaps(mobile.Info.Crushes)) return; - self.Kill(crusher); + self.Kill(crusher, mobile != null ? mobile.Info.CrushDamageTypes : new HashSet()); } bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs b/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs index 89c03836bf..e5a09c6fef 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs @@ -20,6 +20,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Does a suicide attack where it moves next to the target when used in combination with `Explodes`.")] class AttackSuicidesInfo : ConditionalTraitInfo, Requires { + [Desc("Types of damage that this trait causes to self while suiciding. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + [VoiceReference] public readonly string Voice = "Action"; public override object Create(ActorInitializer init) { return new AttackSuicides(init.Self, this); } @@ -82,10 +85,10 @@ namespace OpenRA.Mods.Common.Traits self.QueueActivity(move.MoveToTarget(self, target)); - self.QueueActivity(new CallFunc(() => self.Kill(self))); + self.QueueActivity(new CallFunc(() => self.Kill(self, Info.DamageTypes))); } else if (order.OrderString == "Detonate") - self.Kill(self); + self.Kill(self, Info.DamageTypes); } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index f4b29ad4de..111b236dfd 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -46,6 +46,9 @@ namespace OpenRA.Mods.Common.Traits public WeaponInfo DemolishWeaponInfo { get; private set; } + [Desc("Types of damage that this bridge causes to units over/in path of it while being destroyed/repaired. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public object Create(ActorInitializer init) { return new Bridge(init.Self, this); } public void RulesetLoaded(Ruleset rules, ActorInfo ai) @@ -241,7 +244,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var c in footprint.Keys) foreach (var a in self.World.ActorMap.GetActorsAt(c)) if (a.Info.HasTraitInfo() && !a.Trait().CanEnterCell(c)) - a.Kill(self); + a.Kill(self, info.DamageTypes); } bool NeighbourIsDeadShore(Bridge neighbour) diff --git a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs index 32ec2daaf5..75c4e7332e 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs @@ -30,6 +30,9 @@ namespace OpenRA.Mods.Common.Traits public WeaponInfo DemolishWeaponInfo { get; private set; } + [Desc("Types of damage that this bridge causes to units over/in path of it while being destroyed/repaired. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo weapon; @@ -95,7 +98,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var c in cells) foreach (var a in self.World.ActorMap.GetActorsAt(c)) if (a.Info.HasTraitInfo() && !a.Trait().CanEnterCell(c)) - a.Kill(self); + a.Kill(self, Info.DamageTypes); } void IBridgeSegment.Repair(Actor repairer) diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 29bd75fb74..49cf688d2d 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -57,7 +57,8 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.Play(SoundType.World, Info.CrushSound, crusher.CenterPosition); - self.Kill(crusher); + var crusherMobile = crusher.TraitOrDefault(); + self.Kill(crusher, crusherMobile != null ? crusherMobile.Info.CrushDamageTypes : new HashSet()); } bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) diff --git a/OpenRA.Mods.Common/Traits/Health.cs b/OpenRA.Mods.Common/Traits/Health.cs index 0d82b3edf5..dfce76a641 100644 --- a/OpenRA.Mods.Common/Traits/Health.cs +++ b/OpenRA.Mods.Common/Traits/Health.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -163,9 +164,12 @@ namespace OpenRA.Mods.Common.Traits } } - public void Kill(Actor self, Actor attacker) + public void Kill(Actor self, Actor attacker, HashSet damageTypes = null) { - InflictDamage(self, attacker, new Damage(MaxHP), true); + if (damageTypes == null) + damageTypes = new HashSet(); + + InflictDamage(self, attacker, new Damage(MaxHP, damageTypes), true); } void ITick.Tick(Actor self) diff --git a/OpenRA.Mods.Common/Traits/KillsSelf.cs b/OpenRA.Mods.Common/Traits/KillsSelf.cs index a997486d1c..687ad589b3 100644 --- a/OpenRA.Mods.Common/Traits/KillsSelf.cs +++ b/OpenRA.Mods.Common/Traits/KillsSelf.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -21,6 +22,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("The amount of time (in ticks) before the actor dies. Two values indicate a range between which a random value is chosen.")] public readonly int[] Delay = { 0 }; + [Desc("Types of damage that this trait causes. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + [GrantedConditionReference] [Desc("The condition to grant moments before suiciding.")] public readonly string GrantsCondition = null; @@ -81,7 +85,7 @@ namespace OpenRA.Mods.Common.Traits if (Info.RemoveInstead || !self.Info.HasTraitInfo()) self.Dispose(); else - self.Kill(self); + self.Kill(self, Info.DamageTypes); } } } diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index f3a82e93b4..8401a26639 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -59,6 +59,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("e.g. crate, wall, infantry")] public readonly HashSet Crushes = new HashSet(); + [Desc("Types of damage that are caused while crushing. Leave empty for no damage types.")] + public readonly HashSet CrushDamageTypes = new HashSet(); + public readonly int WaitAverage = 5; public readonly int WaitSpread = 2; diff --git a/OpenRA.Mods.Common/Traits/Parachutable.cs b/OpenRA.Mods.Common/Traits/Parachutable.cs index fdcdf9f426..c86bab7038 100644 --- a/OpenRA.Mods.Common/Traits/Parachutable.cs +++ b/OpenRA.Mods.Common/Traits/Parachutable.cs @@ -22,6 +22,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("If we land on invalid terrain for my actor type should we be killed?")] public readonly bool KilledOnImpassableTerrain = true; + [Desc("Types of damage that this trait causes to self when 'KilledOnImpassableTerrain' is true. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + [Desc("Image where Ground/WaterCorpseSequence is looked up.")] public readonly string Image = "explosion"; @@ -101,7 +104,7 @@ namespace OpenRA.Mods.Common.Traits if (sequence != null && palette != null) self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.OccupiesSpace.CenterPosition, w, info.Image, sequence, palette))); - self.Kill(self); + self.Kill(self, info.DamageTypes); } } } diff --git a/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs b/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs index 4830981855..176c1f9e90 100644 --- a/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs +++ b/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs @@ -219,15 +219,13 @@ namespace OpenRA.Mods.Common.Traits break; var actor = order.Target.Actor; - var health = actor.TraitOrDefault(); var args = order.TargetString.Split(' '); var damageTypes = new HashSet(); foreach (var damageType in args) damageTypes.Add(damageType); - if (health != null) - health.InflictDamage(actor, actor, new Damage(health.HP, damageTypes), true); + actor.Kill(actor, damageTypes); break; } diff --git a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs index 4077e89e91..9ac14f15a2 100644 --- a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Warheads [Desc("How much (raw) damage to deal.")] public readonly int Damage = 0; - [Desc("Types of damage that this warhead causes. Leave empty for no damage.")] + [Desc("Types of damage that this warhead causes. Leave empty for no damage types.")] public readonly HashSet DamageTypes = new HashSet(); [Desc("Damage percentage versus each armortype.")]