diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index c35badf4cf..10da51510c 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -90,6 +90,7 @@
+
diff --git a/OpenRA.Mods.RA/Traits/FrozenUnderFogUpdatedByGps.cs b/OpenRA.Mods.RA/Traits/FrozenUnderFogUpdatedByGps.cs
new file mode 100644
index 0000000000..b403923e61
--- /dev/null
+++ b/OpenRA.Mods.RA/Traits/FrozenUnderFogUpdatedByGps.cs
@@ -0,0 +1,105 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2015 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 OpenRA.Mods.Common.Traits;
+using OpenRA.Primitives;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA.Traits
+{
+ using FrozenActorAction = Action;
+
+ [Desc("Updates frozen actors of actors that change owners, are sold or die whilst having an active GPS power.")]
+ public class FrozenUnderFogUpdatedByGpsInfo : ITraitInfo, Requires
+ {
+ public object Create(ActorInitializer init) { return new FrozenUnderFogUpdatedByGps(init); }
+ }
+
+ public class FrozenUnderFogUpdatedByGps : INotifyOwnerChanged, INotifyActorDisposing, IOnGpsRefreshed
+ {
+ static readonly FrozenActorAction Refresh = (fufubg, fal, gps, fa) =>
+ {
+ // Refreshes the visual state of the frozen actor, so ownership changes can be seen.
+ fa.RefreshState();
+ if (fa.HasRenderables)
+ fa.NeedRenderables = true;
+ };
+ static readonly FrozenActorAction Remove = (fufubg, fal, gps, fa) =>
+ {
+ // Removes the frozen actor. Once done, we no longer need to track GPS updates.
+ fal.Remove(fa);
+ gps.UnregisterForOnGpsRefreshed(fufubg.self, fufubg);
+ };
+
+ class Traits
+ {
+ public readonly FrozenActorLayer FrozenActorLayer;
+ public readonly GpsWatcher GpsWatcher;
+ public Traits(Player player, FrozenUnderFogUpdatedByGps frozenUnderFogUpdatedByGps)
+ {
+ FrozenActorLayer = player.PlayerActor.TraitOrDefault();
+ GpsWatcher = player.PlayerActor.TraitOrDefault();
+ GpsWatcher.RegisterForOnGpsRefreshed(frozenUnderFogUpdatedByGps.self, frozenUnderFogUpdatedByGps);
+ }
+ }
+
+ readonly PlayerDictionary traits;
+ readonly Actor self;
+
+ public FrozenUnderFogUpdatedByGps(ActorInitializer init)
+ {
+ self = init.Self;
+ traits = new PlayerDictionary(init.World, player => new Traits(player, this));
+ }
+
+ public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
+ {
+ ActOnFrozenActorsForAllPlayers(Refresh);
+ }
+
+ public void Disposing(Actor self)
+ {
+ ActOnFrozenActorsForAllPlayers(Remove);
+ }
+
+ public void OnGpsRefresh(Actor self, Player player)
+ {
+ if (self.IsDead)
+ ActOnFrozenActorForPlayer(player, Remove);
+ else
+ ActOnFrozenActorForPlayer(player, Refresh);
+ }
+
+ void ActOnFrozenActorsForAllPlayers(FrozenActorAction action)
+ {
+ for (var playerIndex = 0; playerIndex < traits.Count; playerIndex++)
+ ActOnFrozenActorForTraits(traits[playerIndex], action);
+ }
+
+ void ActOnFrozenActorForPlayer(Player player, FrozenActorAction action)
+ {
+ ActOnFrozenActorForTraits(traits[player], action);
+ }
+
+ void ActOnFrozenActorForTraits(Traits t, FrozenActorAction action)
+ {
+ if (t.FrozenActorLayer == null || t.GpsWatcher == null ||
+ !t.GpsWatcher.Granted || !t.GpsWatcher.GrantedAllies)
+ return;
+
+ var fa = t.FrozenActorLayer.FromID(self.ActorID);
+ if (fa == null)
+ return;
+
+ action(this, t.FrozenActorLayer, t.GpsWatcher, fa);
+ }
+ }
+}
diff --git a/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs b/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs
index 5d411145e0..8a5930dd1b 100644
--- a/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs
+++ b/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs
@@ -25,12 +25,13 @@ namespace OpenRA.Mods.RA.Traits
class GpsWatcher : ISync, IFogVisibilityModifier
{
- [Sync] public bool Launched = false;
- [Sync] public bool GrantedAllies = false;
- [Sync] public bool Granted = false;
- public Player Owner;
+ [Sync] public bool Launched { get; private set; }
+ [Sync] public bool GrantedAllies { get; private set; }
+ [Sync] public bool Granted { get; private set; }
+ public readonly Player Owner;
- List actors = new List { };
+ readonly List actors = new List();
+ readonly HashSet> notifyOnRefresh = new HashSet>();
public GpsWatcher(Player owner) { Owner = owner; }
@@ -49,11 +50,11 @@ namespace OpenRA.Mods.RA.Traits
public void Launch(Actor atek, SupportPowerInfo info)
{
atek.World.Add(new DelayedAction(((GpsPowerInfo)info).RevealDelay * 25,
- () =>
- {
- Launched = true;
- RefreshGps(atek);
- }));
+ () =>
+ {
+ Launched = true;
+ RefreshGps(atek);
+ }));
}
public void RefreshGps(Actor atek)
@@ -69,11 +70,18 @@ namespace OpenRA.Mods.RA.Traits
void RefreshGranted()
{
+ var wasGranted = Granted;
+ var wasGrantedAllies = GrantedAllies;
+
Granted = actors.Count > 0 && Launched;
GrantedAllies = Owner.World.ActorsHavingTrait(g => g.Granted).Any(p => p.Owner.IsAlliedWith(Owner));
if (Granted || GrantedAllies)
Owner.Shroud.ExploreAll(Owner.World);
+
+ if (wasGranted != Granted || wasGrantedAllies != GrantedAllies)
+ foreach (var tp in notifyOnRefresh.ToList())
+ tp.Trait.OnGpsRefresh(tp.Actor, Owner);
}
public bool HasFogVisibility()
@@ -89,8 +97,20 @@ namespace OpenRA.Mods.RA.Traits
return gpsDot.IsDotVisible(Owner);
}
+
+ public void RegisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)
+ {
+ notifyOnRefresh.Add(new TraitPair(actor, toBeNotified));
+ }
+
+ public void UnregisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)
+ {
+ notifyOnRefresh.Remove(new TraitPair(actor, toBeNotified));
+ }
}
+ interface IOnGpsRefreshed { void OnGpsRefresh(Actor self, Player player); }
+
class GpsPowerInfo : SupportPowerInfo
{
public readonly int RevealDelay = 0;
diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml
index 6e21894c14..d5cd53210c 100644
--- a/mods/ra/rules/defaults.yaml
+++ b/mods/ra/rules/defaults.yaml
@@ -430,6 +430,7 @@
Guardable:
Range: 3c0
FrozenUnderFog:
+ FrozenUnderFogUpdatedByGps:
Tooltip:
GenericName: Structure
Demolishable:
@@ -499,6 +500,7 @@
SellSounds: cashturn.aud
Guardable:
FrozenUnderFog:
+ FrozenUnderFogUpdatedByGps:
Health:
HP: 100
Shape: Rectangle