diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 850b5e92ff..0ed83fa185 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -104,6 +104,7 @@ namespace OpenRA.Traits public interface INotifyEffectiveOwnerChanged { void OnEffectiveOwnerChanged(Actor self, Player oldEffectiveOwner, Player newEffectiveOwner); } public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); } public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator); } + public interface INotifyDiscovered { void OnDiscovered(Actor self, Player discoverer, bool playNotification); } public interface IDisableMove { bool MoveDisabled(Actor self); } public interface ISeedableResource { void Seed(Actor self); } diff --git a/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs b/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs index 257995df43..115bfce887 100644 --- a/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs +++ b/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs @@ -8,9 +8,7 @@ */ #endregion -using System; using System.Collections.Generic; -using System.Drawing; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -25,13 +23,13 @@ namespace OpenRA.Mods.Common.Traits [Desc("Minimal ticks in-between notifications.")] public readonly int NotificationInterval = 750; - public object Create(ActorInitializer init) { return new EnemyWatcher(init.Self, this); } + public object Create(ActorInitializer init) { return new EnemyWatcher(this); } } class EnemyWatcher : ITick { readonly EnemyWatcherInfo info; - readonly Lazy radarPings; + readonly HashSet discoveredPlayers; bool announcedAny; int rescanInterval; @@ -40,15 +38,16 @@ namespace OpenRA.Mods.Common.Traits HashSet visibleActorIds; HashSet playedNotifications; - public EnemyWatcher(Actor self, EnemyWatcherInfo info) + public EnemyWatcher(EnemyWatcherInfo info) { lastKnownActorIds = new HashSet(); + discoveredPlayers = new HashSet(); this.info = info; rescanInterval = info.ScanInterval; ticksBeforeNextNotification = info.NotificationInterval; - radarPings = Exts.Lazy(() => self.World.WorldActor.Trait()); } + // Here self is the player actor public void Tick(Actor self) { // TODO: Make the AI handle such notifications and remove Owner.IsBot from this check @@ -70,9 +69,9 @@ namespace OpenRA.Mods.Common.Traits foreach (var actor in self.World.ActorsWithTrait()) { - // We only care about enemy actors (creeps should be enemies) - if ((actor.Actor.EffectiveOwner != null && self.Owner.Stances[actor.Actor.EffectiveOwner.Owner] != Stance.Enemy) - || self.Owner.Stances[actor.Actor.Owner] != Stance.Enemy) + // We don't want notifications for allied actors + if ((actor.Actor.EffectiveOwner != null && self.Owner.Stances[actor.Actor.EffectiveOwner.Owner] == Stance.Ally) + || self.Owner.Stances[actor.Actor.Owner] == Stance.Ally) continue; if (actor.Actor.IsDead || !actor.Actor.IsInWorld) @@ -88,12 +87,28 @@ namespace OpenRA.Mods.Common.Traits if (lastKnownActorIds.Contains(actor.Actor.ActorID)) continue; + var notificationPlayed = playedNotifications.Contains(actor.Trait.Info.Notification); + + // Notify the actor that he has been discovered + foreach (var trait in actor.Actor.TraitsImplementing()) + trait.OnDiscovered(actor.Actor, self.Owner, !notificationPlayed); + + var discoveredPlayer = actor.Actor.Owner; + if (!discoveredPlayers.Contains(discoveredPlayer)) + { + // Notify the actor's owner that he has been discovered + foreach (var trait in discoveredPlayer.PlayerActor.TraitsImplementing()) + trait.OnDiscovered(actor.Actor, self.Owner, false); + + discoveredPlayers.Add(discoveredPlayer); + } + // We have already played this type of notification - if (playedNotifications.Contains(actor.Trait.Info.Notification)) + if (notificationPlayed) continue; - if (self.Owner == self.World.RenderPlayer) - Announce(self, actor); + playedNotifications.Add(actor.Trait.Info.Notification); + announcedAny = true; } if (announcedAny) @@ -101,18 +116,5 @@ namespace OpenRA.Mods.Common.Traits lastKnownActorIds = visibleActorIds; } - - void Announce(Actor self, TraitPair announce) - { - // Audio notification - Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", announce.Trait.Info.Notification, self.Owner.Country.Race); - - // Radar notificaion - if (announce.Trait.Info.PingRadar && radarPings.Value != null) - radarPings.Value.Add(() => true, announce.Actor.CenterPosition, Color.Red, 50); - - playedNotifications.Add(announce.Trait.Info.Notification); - announcedAny = true; - } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs b/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs index f7277d645a..51a0fa8c7e 100644 --- a/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs +++ b/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs @@ -8,6 +8,8 @@ */ #endregion +using System; +using System.Drawing; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,18 +20,35 @@ namespace OpenRA.Mods.Common.Traits [Desc("Should there be a radar ping on enemies' radar at the actor's location when they see him")] public readonly bool PingRadar = false; - public readonly string Notification = "EnemyUnitSighted"; + public readonly string Notification = null; - public object Create(ActorInitializer init) { return new AnnounceOnSeen(this); } + public object Create(ActorInitializer init) { return new AnnounceOnSeen(init.Self, this); } } - public class AnnounceOnSeen + public class AnnounceOnSeen : INotifyDiscovered { public readonly AnnounceOnSeenInfo Info; - public AnnounceOnSeen(AnnounceOnSeenInfo info) + readonly Lazy radarPings; + + public AnnounceOnSeen(Actor self, AnnounceOnSeenInfo info) { Info = info; + radarPings = Exts.Lazy(() => self.World.WorldActor.Trait()); + } + + public void OnDiscovered(Actor self, Player discoverer, bool playNotification) + { + if (!playNotification || discoverer != self.World.RenderPlayer) + return; + + // Audio notification + if (discoverer != null && !string.IsNullOrEmpty(Info.Notification)) + Sound.PlayNotification(self.World.Map.Rules, discoverer, "Speech", Info.Notification, discoverer.Country.Race); + + // Radar notificaion + if (Info.PingRadar && radarPings.Value != null) + radarPings.Value.Add(() => true, self.CenterPosition, Color.Red, 50); } } } \ No newline at end of file