From e2832bb3d7d333d39995ce7df6a2769195c9998d Mon Sep 17 00:00:00 2001 From: penev92 Date: Mon, 22 Dec 2014 04:06:45 +0200 Subject: [PATCH] Implement EnemyWatcher and AnnounceOnSeen traits --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 2 + .../Traits/Player/EnemyWatcher.cs | 115 ++++++++++++++++++ .../Traits/Sound/AnnounceOnSeen.cs | 35 ++++++ 3 files changed, 152 insertions(+) create mode 100644 OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs create mode 100644 OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 3414364aad..1795252c1f 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -256,6 +256,7 @@ + @@ -328,6 +329,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs b/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs new file mode 100644 index 0000000000..e703837a9e --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs @@ -0,0 +1,115 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Tracks neutral and enemy actors' visibility and notifies the player.", + "Attach this to the player actor.")] + class EnemyWatcherInfo : ITraitInfo + { + [Desc("Interval in ticks between scanning for enemies.")] + public readonly int ScanInterval = 25; + + [Desc("Minimal interval in ticks between notifications.")] + public readonly int NotificationInterval = 200; + + public object Create(ActorInitializer init) { return new EnemyWatcher(init.Self, this); } + } + + class EnemyWatcher : ITick + { + readonly EnemyWatcherInfo info; + readonly Lazy radarPings; + + bool announcedAny; + int rescanInterval; + int ticksBeforeNextNotification; + HashSet lastKnownActorIds; + HashSet visibleActorIds; + HashSet playedNotifications; + + public EnemyWatcher(Actor self, EnemyWatcherInfo info) + { + lastKnownActorIds = new HashSet(); + this.info = info; + rescanInterval = info.ScanInterval; + ticksBeforeNextNotification = info.NotificationInterval; + radarPings = Exts.Lazy(() => self.World.WorldActor.Trait()); + } + + public void Tick(Actor self) + { + // TODO: Make the AI handle such notifications and remove Owner.IsBot from this check + // Disable notifications for AI and neutral players (creeps) and for spectators + if (self.Owner.Shroud.Disabled || self.Owner.IsBot || !self.Owner.Playable || self.Owner.PlayerReference.Spectating) + return; + + rescanInterval--; + ticksBeforeNextNotification--; + + if (rescanInterval > 0 || ticksBeforeNextNotification > 0) + return; + + rescanInterval = info.ScanInterval; + + announcedAny = false; + visibleActorIds = new HashSet(); + playedNotifications = new HashSet(); + + 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) + continue; + + // The actor is not currently visible + if (!self.Owner.Shroud.IsVisible(actor.Actor)) + continue; + + visibleActorIds.Add(actor.Actor.ActorID); + + // We already know about this actor + if (lastKnownActorIds.Contains(actor.Actor.ActorID)) + continue; + + // We have already played this type of notification + if (playedNotifications.Contains(actor.Trait.Info.Notification)) + continue; + + Announce(self, actor); + } + + if (announcedAny) + ticksBeforeNextNotification = info.NotificationInterval; + + lastKnownActorIds = visibleActorIds; + } + + void Announce(Actor self, TraitPair announce) + { + // Audio notification + if (self.World.LocalPlayer != null) + Sound.PlayNotification(self.World.Map.Rules, self.World.LocalPlayer, "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 new file mode 100644 index 0000000000..f7277d645a --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs @@ -0,0 +1,35 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Players will be notified when this actor becomes visible to them.")] + public class AnnounceOnSeenInfo : ITraitInfo + { + [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 object Create(ActorInitializer init) { return new AnnounceOnSeen(this); } + } + + public class AnnounceOnSeen + { + public readonly AnnounceOnSeenInfo Info; + + public AnnounceOnSeen(AnnounceOnSeenInfo info) + { + Info = info; + } + } +} \ No newline at end of file