diff --git a/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs b/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs index f883b43058..a6ff452b9a 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs @@ -12,7 +12,6 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -25,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits } [Desc("Attach this to a unit to enable dynamic conditions by warheads, experience, crates, support powers, etc.")] - public class ConditionManagerInfo : TraitInfo, Requires { } + public class ConditionManagerInfo : TraitInfo, Requires { } public class ConditionManager : INotifyCreated, ITick { @@ -47,8 +46,8 @@ namespace OpenRA.Mods.Common.Traits class ConditionState { - /// Traits that have registered to be notified when this condition changes. - public readonly List Consumers = new List(); + /// Delegates that have registered to be notified when this condition changes. + public readonly List Notifiers = new List(); /// Unique integers identifying granted instances of the condition. public readonly HashSet Tokens = new HashSet(); @@ -76,21 +75,24 @@ namespace OpenRA.Mods.Common.Traits state = new Dictionary(); readOnlyConditionCache = new ReadOnlyDictionary(conditionCache); - var allConsumers = new HashSet(); + var allObservers = new HashSet(); var allWatchers = self.TraitsImplementing().ToList(); - foreach (var consumer in self.TraitsImplementing()) + foreach (var provider in self.TraitsImplementing()) { - allConsumers.Add(consumer); - foreach (var condition in consumer.Conditions) + foreach (var variableUser in provider.GetVariableObservers()) { - var cs = state.GetOrAdd(condition); - cs.Consumers.Add(consumer); - foreach (var w in allWatchers) - if (w.Condition == condition) - cs.Watchers.Add(w); + allObservers.Add(variableUser.Notifier); + foreach (var variable in variableUser.Variables) + { + var cs = state.GetOrAdd(variable); + cs.Notifiers.Add(variableUser.Notifier); + foreach (var w in allWatchers) + if (w.Condition == variable) + cs.Watchers.Add(w); - conditionCache[condition] = 0; + conditionCache[variable] = 0; + } } } @@ -106,8 +108,8 @@ namespace OpenRA.Mods.Common.Traits } // Update all traits with their initial condition state - foreach (var consumer in allConsumers) - consumer.ConditionsChanged(self, readOnlyConditionCache); + foreach (var consumer in allObservers) + consumer(self, readOnlyConditionCache); } void UpdateConditionState(Actor self, string condition, int token, bool isRevoke) @@ -123,8 +125,8 @@ namespace OpenRA.Mods.Common.Traits conditionCache[condition] = conditionState.Tokens.Count; - foreach (var t in conditionState.Consumers) - t.ConditionsChanged(self, readOnlyConditionCache); + foreach (var notify in conditionState.Notifiers) + notify(self, readOnlyConditionCache); } /// Grants a specified condition. diff --git a/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs b/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs index 0e7e8986fa..dc942ba0a6 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs @@ -10,14 +10,13 @@ #endregion using System.Collections.Generic; -using System.Linq; using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { /// Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.) - public abstract class ConditionalTraitInfo : IConditionConsumerInfo, IRulesetLoaded + public abstract class ConditionalTraitInfo : IObservesVariablesInfo, IRulesetLoaded { static readonly IReadOnlyDictionary NoConditions = new ReadOnlyDictionary(new Dictionary()); @@ -43,19 +42,15 @@ namespace OpenRA.Mods.Common.Traits /// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor. /// TraitEnabled will be called at creation if the trait starts enabled or does not use conditions. /// - public abstract class ConditionalTrait : IConditionConsumer, IDisabledTrait, INotifyCreated, ISync where InfoType : ConditionalTraitInfo + public abstract class ConditionalTrait : IObservesVariables, IDisabledTrait, INotifyCreated, ISync where InfoType : ConditionalTraitInfo { public readonly InfoType Info; - IEnumerable IConditionConsumer.Conditions + // Overrides must call `base.GetVariableObservers()` to avoid breaking RequiresCondition. + public virtual IEnumerable GetVariableObservers() { - get - { - if (Info.RequiresCondition != null) - return Info.RequiresCondition.Variables; - - return Enumerable.Empty(); - } + if (Info.RequiresCondition != null) + yield return new VariableObserver(RequiredConditionsChanged, Info.RequiresCondition.Variables); } [Sync] public bool IsTraitDisabled { get; private set; } @@ -65,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits Info = info; // Conditional traits will be enabled (if appropriate) by the ConditionManager - // calling IConditionConsumer.ConditionsChanged at the end of INotifyCreated. + // calling ConditionConsumers at the end of INotifyCreated. IsTraitDisabled = Info.RequiresCondition != null; } @@ -77,7 +72,7 @@ namespace OpenRA.Mods.Common.Traits void INotifyCreated.Created(Actor self) { Created(self); } - void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary conditions) + void RequiredConditionsChanged(Actor self, IReadOnlyDictionary conditions) { if (Info.RequiresCondition == null) return; diff --git a/OpenRA.Mods.Common/Traits/Pluggable.cs b/OpenRA.Mods.Common/Traits/Pluggable.cs index 60a937b748..b0bfd0e639 100644 --- a/OpenRA.Mods.Common/Traits/Pluggable.cs +++ b/OpenRA.Mods.Common/Traits/Pluggable.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; +using OpenRA.Primitives; using OpenRA.Support; using OpenRA.Traits; @@ -44,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new Pluggable(init, this); } } - public class Pluggable : IConditionConsumer, INotifyCreated + public class Pluggable : IObservesVariables, INotifyCreated { public readonly PluggableInfo Info; @@ -114,12 +115,12 @@ namespace OpenRA.Mods.Common.Traits active = null; } - IEnumerable IConditionConsumer.Conditions { get { return Info.ConsumedConditions; } } - - void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary conditions) + IEnumerable IObservesVariables.GetVariableObservers() { foreach (var req in Info.Requirements) - plugTypesAvailability[req.Key] = req.Value.Evaluate(conditions); + yield return new VariableObserver( + (self, variables) => plugTypesAvailability[req.Key] = req.Value.Evaluate(variables), + req.Value.Variables); } } diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 175dedf20c..0fb9d5261b 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -105,12 +105,23 @@ namespace OpenRA.Mods.Common.Traits public interface INotifyPassengerExited { void OnPassengerExited(Actor self, Actor passenger); } [RequireExplicitImplementation] - public interface IConditionConsumerInfo : ITraitInfo { } + public interface IObservesVariablesInfo : ITraitInfo { } - public interface IConditionConsumer + public delegate void VariableObserverNotifier(Actor self, IReadOnlyDictionary variables); + public struct VariableObserver { - IEnumerable Conditions { get; } - void ConditionsChanged(Actor self, IReadOnlyDictionary conditions); + public VariableObserverNotifier Notifier; + public IEnumerable Variables; + public VariableObserver(VariableObserverNotifier notifier, IEnumerable variables) + { + Notifier = notifier; + Variables = variables; + } + } + + public interface IObservesVariables + { + IEnumerable GetVariableObservers(); } public interface INotifyHarvesterAction