diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 802806a1f9..fddf393d21 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -495,6 +495,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs b/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs index dc942ba0a6..46d4cf30dc 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/ConditionalTrait.cs @@ -15,10 +15,10 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - /// Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.) + /// Use as base class for *Info to subclass of ConditionalTrait. (See ConditionalTrait.) public abstract class ConditionalTraitInfo : IObservesVariablesInfo, IRulesetLoaded { - static readonly IReadOnlyDictionary NoConditions = new ReadOnlyDictionary(new Dictionary()); + protected static readonly IReadOnlyDictionary NoConditions = new ReadOnlyDictionary(new Dictionary()); [ConsumedConditionReference] [Desc("Boolean expression defining the condition to enable this trait.")] @@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.Traits /// /// Abstract base for enabling and disabling trait using conditions. - /// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor. + /// Requires basing *Info on ConditionalTraitInfo 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 : IObservesVariables, IDisabledTrait, INotifyCreated, ISync where InfoType : ConditionalTraitInfo diff --git a/OpenRA.Mods.Common/Traits/Conditions/PausableConditionalTrait.cs b/OpenRA.Mods.Common/Traits/Conditions/PausableConditionalTrait.cs new file mode 100644 index 0000000000..d6203e3a30 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Conditions/PausableConditionalTrait.cs @@ -0,0 +1,81 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Primitives; +using OpenRA.Support; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + /// Use as base class for *Info to subclass of PausableConditionalTrait. (See PausableConditionalTrait.) + public abstract class PausableConditionalTraitInfo : ConditionalTraitInfo + { + [ConsumedConditionReference] + [Desc("Boolean expression defining the condition to pause this trait.")] + public readonly BooleanExpression PauseOnCondition = null; + + public bool PausedByDefault { get; private set; } + + public override void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + base.RulesetLoaded(rules, ai); + PausedByDefault = PauseOnCondition != null && PauseOnCondition.Evaluate(NoConditions); + } + } + + /// + /// Abstract base for enabling and disabling trait using conditions. + /// Requires basing *Info on PausableConditionalTraitInfo and using base(info) constructor. + /// TraitResumed will be called at creation if the trait starts not paused or does not have a pause condition. + /// + public abstract class PausableConditionalTrait : ConditionalTrait where InfoType : PausableConditionalTraitInfo + { + [Sync] public bool IsTraitPaused { get; private set; } + + protected PausableConditionalTrait(InfoType info) : base(info) { IsTraitPaused = info.PausedByDefault; } + + protected override void Created(Actor self) + { + base.Created(self); + if (Info.PauseOnCondition == null) + TraitResumed(self); + } + + // Overrides must call `base.GetVariableObservers()` to avoid breaking RequiresCondition or PauseOnCondition. + public override IEnumerable GetVariableObservers() + { + foreach (var observer in base.GetVariableObservers()) + yield return observer; + + if (Info.PauseOnCondition != null) + yield return new VariableObserver(PauseConditionsChanged, Info.PauseOnCondition.Variables); + } + + void PauseConditionsChanged(Actor self, IReadOnlyDictionary conditions) + { + var wasPaused = IsTraitPaused; + IsTraitPaused = Info.PauseOnCondition.Evaluate(conditions); + + if (IsTraitPaused != wasPaused) + { + if (wasPaused) + TraitResumed(self); + else + TraitPaused(self); + } + } + + // Subclasses can add pause support by querying IsTraitPaused and/or overriding these methods. + protected virtual void TraitResumed(Actor self) { } + protected virtual void TraitPaused(Actor self) { } + } +}