diff --git a/OpenRA.Game/Traits/Health.cs b/OpenRA.Game/Traits/Health.cs index bddc9c9c74..2d18176c1f 100755 --- a/OpenRA.Game/Traits/Health.cs +++ b/OpenRA.Game/Traits/Health.cs @@ -15,9 +15,15 @@ namespace OpenRA.Traits { public class HealthInfo : ITraitInfo, UsesInit { + [Desc("HitPoints")] public readonly int HP = 0; + [Desc("Physical size of the unit used for damage calculations. Impacts within this radius apply full damage")] public readonly WRange Radius = new WRange(426); + + [Desc("Don't trigger interfaces such as AnnounceOnKill.")] + public readonly bool NotifyAppliedDamage = true; + public virtual object Create(ActorInitializer init) { return new Health(init, this); } } @@ -92,7 +98,7 @@ namespace OpenRA.Traits foreach (var nd in self.TraitsImplementing()) nd.DamageStateChanged(self, ai); - if (repairer != null && repairer.IsInWorld && !repairer.IsDead()) + if (Info.NotifyAppliedDamage && repairer != null && repairer.IsInWorld && !repairer.IsDead()) foreach (var nd in repairer.TraitsImplementing() .Concat(repairer.Owner.PlayerActor.TraitsImplementing())) nd.AppliedDamage(repairer, self, ai); @@ -135,7 +141,7 @@ namespace OpenRA.Traits foreach (var nd in self.TraitsImplementing()) nd.DamageStateChanged(self, ai); - if (attacker != null && attacker.IsInWorld && !attacker.IsDead()) + if (Info.NotifyAppliedDamage && attacker != null && attacker.IsInWorld && !attacker.IsDead()) foreach (var nd in attacker.TraitsImplementing() .Concat(attacker.Owner.PlayerActor.TraitsImplementing())) nd.AppliedDamage(attacker, self, ai); diff --git a/OpenRA.Mods.RA/Activities/LayMines.cs b/OpenRA.Mods.RA/Activities/LayMines.cs index d1f60de62d..3aa7cdb581 100644 --- a/OpenRA.Mods.RA/Activities/LayMines.cs +++ b/OpenRA.Mods.RA/Activities/LayMines.cs @@ -26,8 +26,9 @@ namespace OpenRA.Mods.RA.Activities var limitedAmmo = self.TraitOrDefault(); if (!limitedAmmo.HasAmmo()) { + var info = self.Info.Traits.Get(); // rearm & repair at fix, then back out here to refill the minefield some more - var buildings = self.Info.Traits.Get().RearmBuildings; + var buildings = info.RearmBuildings; var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally && buildings.Contains(a.Info.Name)) .ClosestTo(self); @@ -38,26 +39,27 @@ namespace OpenRA.Mods.RA.Activities return Util.SequenceActivities( new MoveAdjacentTo(self, Target.FromActor(rearmTarget)), movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget), - new Rearm(self), + new Rearm(self, info.RearmSound), new Repair(rearmTarget), this); } var ml = self.Trait(); - if (ml.Minefield.Contains(self.Location) && - ShouldLayMine(self, self.Location)) + if (ml.Minefield.Contains(self.Location) && ShouldLayMine(self, self.Location)) { LayMine(self); return Util.SequenceActivities(new Wait(20), this); // a little wait after placing each mine, for show } if (ml.Minefield.Length > 0) + { for (var n = 0; n < 20; n++) // dont get stuck forever here { var p = ml.Minefield.Random(self.World.SharedRandom); if (ShouldLayMine(self, p)) return Util.SequenceActivities( movement.MoveTo(p, 0), this ); } + } // TODO: return somewhere likely to be safe (near fix) so we're not sitting out in the minefield. diff --git a/OpenRA.Mods.RA/Activities/Rearm.cs b/OpenRA.Mods.RA/Activities/Rearm.cs index ce9a37b327..c3bb48aca7 100644 --- a/OpenRA.Mods.RA/Activities/Rearm.cs +++ b/OpenRA.Mods.RA/Activities/Rearm.cs @@ -19,18 +19,21 @@ namespace OpenRA.Mods.RA.Activities readonly LimitedAmmo limitedAmmo; int ticksPerPip = 25 * 2; int remainingTicks = 25 * 2; + string sound = null; - public Rearm(Actor self) + public Rearm(Actor self, string sound = null) { limitedAmmo = self.TraitOrDefault(); if (limitedAmmo != null) ticksPerPip = limitedAmmo.ReloadTimePerAmmo(); remainingTicks = ticksPerPip; + this.sound = sound; } public override Activity Tick(Actor self) { - if (IsCanceled || limitedAmmo == null) return NextActivity; + if (IsCanceled || limitedAmmo == null) + return NextActivity; if (--remainingTicks == 0) { @@ -44,6 +47,8 @@ namespace OpenRA.Mods.RA.Activities return NextActivity; hostBuilding.Trait().PlayCustomAnim(hostBuilding, "active"); + if (sound != null) + Sound.Play(sound, self.CenterPosition); remainingTicks = limitedAmmo.ReloadTimePerAmmo(); } diff --git a/OpenRA.Mods.RA/AnnounceOnBuild.cs b/OpenRA.Mods.RA/AnnounceOnBuild.cs index 8b72aa8070..315be243de 100644 --- a/OpenRA.Mods.RA/AnnounceOnBuild.cs +++ b/OpenRA.Mods.RA/AnnounceOnBuild.cs @@ -12,6 +12,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { + [Desc("Play the Build voice of this actor when trained.")] public class AnnounceOnBuildInfo : ITraitInfo { public object Create(ActorInitializer init) { return new AnnounceOnBuild(init.self); } diff --git a/OpenRA.Mods.RA/AnnounceOnKill.cs b/OpenRA.Mods.RA/AnnounceOnKill.cs index 424f4d5906..fc5ef1c245 100644 --- a/OpenRA.Mods.RA/AnnounceOnKill.cs +++ b/OpenRA.Mods.RA/AnnounceOnKill.cs @@ -12,15 +12,36 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class AnnounceOnKillInfo : TraitInfo {} + [Desc("Play the Kill voice of this actor when eliminating enemies.")] + public class AnnounceOnKillInfo : ITraitInfo + { + [Desc("Minimum duration (in seconds) between sound events.")] + public readonly int Interval = 5; + + public object Create(ActorInitializer init) { return new AnnounceOnKill(init.self, this); } + } public class AnnounceOnKill : INotifyAppliedDamage { + readonly AnnounceOnKillInfo info; + + int lastAnnounce; + + public AnnounceOnKill(Actor self, AnnounceOnKillInfo info) + { + this.info = info; + lastAnnounce = -info.Interval * 25; + } + public void AppliedDamage(Actor self, Actor damaged, AttackInfo e) { - if (e.DamageState == DamageState.Dead) - Sound.PlayVoice("Kill", self, self.Owner.Country.Race); + if (e.DamageState == DamageState.Dead && damaged != e.Attacker) // don't notify suicides + { + if (self.World.WorldTick - lastAnnounce > info.Interval * 25) + Sound.PlayVoice("Kill", self, self.Owner.Country.Race); + + lastAnnounce = self.World.WorldTick; + } } } } - diff --git a/OpenRA.Mods.RA/C4Demolition.cs b/OpenRA.Mods.RA/C4Demolition.cs index 1c386410ff..3760ec3a19 100644 --- a/OpenRA.Mods.RA/C4Demolition.cs +++ b/OpenRA.Mods.RA/C4Demolition.cs @@ -30,6 +30,8 @@ namespace OpenRA.Mods.RA public readonly int FlashInterval = 4; [Desc("Duration of each flash")] public readonly int FlashDuration = 3; + [Desc("Voice string when planting explosive charges.")] + public readonly string Voice = "Attack"; public object Create(ActorInitializer init) { return new C4Demolition(this); } } @@ -82,7 +84,7 @@ namespace OpenRA.Mods.RA public string VoicePhraseForOrder(Actor self, Order order) { - return order.OrderString == "C4" ? "Attack" : null; + return order.OrderString == "C4" ? info.Voice : null; } class C4DemolitionOrderTargeter : UnitOrderTargeter diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index c4f9a17789..13b8128a6b 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -23,6 +23,8 @@ namespace OpenRA.Mods.RA [ActorReference] public readonly string Mine = "minv"; [ActorReference] public readonly string[] RearmBuildings = { "fix" }; + public readonly string RearmSound = "minelay1.aud"; + public readonly float MinefieldDepth = 1.5f; public object Create(ActorInitializer init) { return new Minelayer(init.self); } @@ -30,7 +32,7 @@ namespace OpenRA.Mods.RA class Minelayer : IIssueOrder, IResolveOrder, IPostRenderSelection, ISync { - /* [Sync] when sync can cope with arrays! */ + /* TODO: [Sync] when sync can cope with arrays! */ public CPos[] Minefield = null; [Sync] CPos minefieldStart; Actor self; diff --git a/mods/ra/maps/desert-shellmap/map.yaml b/mods/ra/maps/desert-shellmap/map.yaml index 711f967fa9..20e421c712 100644 --- a/mods/ra/maps/desert-shellmap/map.yaml +++ b/mods/ra/maps/desert-shellmap/map.yaml @@ -1338,6 +1338,8 @@ Rules: Percentage: 0 InvulnerabilityUpgrade@UNKILLABLE: RequiresUpgrade: unkillable + E7: + -Selectable: Sequences: diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index c96304ea82..67a182d115 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -250,6 +250,7 @@ E7: Range: 6c0 C4Demolition: C4Delay: 45 + Voice: Demolish Passenger: PipType: Red Armament@PRIMARY: @@ -264,6 +265,8 @@ E7: TakeCover: RenderInfantry: IdleAnimations: idle1,idle2 + AnnounceOnBuild: + AnnounceOnKill: MEDI: Inherits: ^Infantry diff --git a/mods/ra/rules/misc.yaml b/mods/ra/rules/misc.yaml index 08bbc5973b..db63c0f944 100644 --- a/mods/ra/rules/misc.yaml +++ b/mods/ra/rules/misc.yaml @@ -7,6 +7,7 @@ MINP: Weapon: APMine Health: HP: 100 + NotifyAppliedDamage: false Armor: Type: Light RenderSimple: @@ -35,6 +36,7 @@ MINV: Weapon: ATMine Health: HP: 100 + NotifyAppliedDamage: false Armor: Type: Light RenderSimple: diff --git a/mods/ra/voices.yaml b/mods/ra/voices.yaml index 3adc13e76d..39d5ebd718 100644 --- a/mods/ra/voices.yaml +++ b/mods/ra/voices.yaml @@ -46,11 +46,14 @@ MechanicVoice: TanyaVoice: Voices: Select: yo1,yes1,yeah1 - Move: rokroll1,onit1,cmon1 - Attack: tuffguy1,bombit1,gotit1 + Move: onit1,cmon1,rokroll1 + Attack: tuffguy1,bombit1 Die: tandeth1 Burned: tandeth1 Zapped: tandeth1 + Build: laugh1 + Kill: gotit1,lefty1 + Demolish: keepem1,tuffguy1 DogVoice: Voices: diff --git a/mods/ra/weapons.yaml b/mods/ra/weapons.yaml index 7e1864719e..dda8f763bf 100644 --- a/mods/ra/weapons.yaml +++ b/mods/ra/weapons.yaml @@ -1093,7 +1093,7 @@ DepthCharge: SmudgeType: Crater Warhead@3Eff: CreateEffect Explosion: large_splash - ImpactSound: splash9.aud + ImpactSound: h2obomb2.aud Warhead@4EffHit: CreateEffect Explosion: small_explosion ImpactSound: kaboom15.aud