Introduce FrozenUnderFogUpdatedByGps and IOnGpsRefreshed.
This trait uses the new interface to register with the GpsWatcher to get notified when the GPS state changes. It needs to register as if the actor is destroyed, the GpsWatcher can no longer query actors in the world to get the trait. This is no good as the trait needs to perform actions after the death of its parent actor. When the GPS is enabled, the trait will track ownership changes and update the frozen actor (just like if there was only a GPS dot, it would change colour) and it will also remove frozen actors if the real actor dies or is sold.
This commit is contained in:
@@ -90,6 +90,7 @@
|
||||
<Compile Include="Traits\Chronoshiftable.cs" />
|
||||
<Compile Include="Traits\Cloneable.cs" />
|
||||
<Compile Include="Traits\Disguise.cs" />
|
||||
<Compile Include="Traits\FrozenUnderFogUpdatedByGps.cs" />
|
||||
<Compile Include="Traits\HarvesterHuskModifier.cs" />
|
||||
<Compile Include="Traits\Infiltration\InfiltrateForCash.cs" />
|
||||
<Compile Include="Traits\Infiltration\InfiltrateForExploration.cs" />
|
||||
|
||||
105
OpenRA.Mods.RA/Traits/FrozenUnderFogUpdatedByGps.cs
Normal file
105
OpenRA.Mods.RA/Traits/FrozenUnderFogUpdatedByGps.cs
Normal file
@@ -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<FrozenUnderFogUpdatedByGps, FrozenActorLayer, GpsWatcher, FrozenActor>;
|
||||
|
||||
[Desc("Updates frozen actors of actors that change owners, are sold or die whilst having an active GPS power.")]
|
||||
public class FrozenUnderFogUpdatedByGpsInfo : ITraitInfo, Requires<FrozenUnderFogInfo>
|
||||
{
|
||||
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<FrozenActorLayer>();
|
||||
GpsWatcher = player.PlayerActor.TraitOrDefault<GpsWatcher>();
|
||||
GpsWatcher.RegisterForOnGpsRefreshed(frozenUnderFogUpdatedByGps.self, frozenUnderFogUpdatedByGps);
|
||||
}
|
||||
}
|
||||
|
||||
readonly PlayerDictionary<Traits> traits;
|
||||
readonly Actor self;
|
||||
|
||||
public FrozenUnderFogUpdatedByGps(ActorInitializer init)
|
||||
{
|
||||
self = init.Self;
|
||||
traits = new PlayerDictionary<Traits>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Actor> actors = new List<Actor> { };
|
||||
readonly List<Actor> actors = new List<Actor>();
|
||||
readonly HashSet<TraitPair<IOnGpsRefreshed>> notifyOnRefresh = new HashSet<TraitPair<IOnGpsRefreshed>>();
|
||||
|
||||
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<GpsWatcher>(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<IOnGpsRefreshed>(actor, toBeNotified));
|
||||
}
|
||||
|
||||
public void UnregisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)
|
||||
{
|
||||
notifyOnRefresh.Remove(new TraitPair<IOnGpsRefreshed>(actor, toBeNotified));
|
||||
}
|
||||
}
|
||||
|
||||
interface IOnGpsRefreshed { void OnGpsRefresh(Actor self, Player player); }
|
||||
|
||||
class GpsPowerInfo : SupportPowerInfo
|
||||
{
|
||||
public readonly int RevealDelay = 0;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user