diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/EnumContainsConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/EnumContainsConditionOperator.cs new file mode 100644 index 000000000..dd79f14cb --- /dev/null +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/EnumContainsConditionOperator.cs @@ -0,0 +1,15 @@ +using System; + +namespace Artemis.Core +{ + internal class EnumContainsConditionOperator : ConditionOperator + { + public override string Description => "Contains"; + public override string Icon => "Contain"; + + public override bool Evaluate(Enum a, Enum b) + { + return a != null && b != null && a.HasFlag(b); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/EnumNotContainsConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/EnumNotContainsConditionOperator.cs new file mode 100644 index 000000000..ef78b8470 --- /dev/null +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/EnumNotContainsConditionOperator.cs @@ -0,0 +1,15 @@ +using System; + +namespace Artemis.Core +{ + internal class EnumNotContainsConditionOperator : ConditionOperator + { + public override string Description => "Does not contain"; + public override string Icon => "FormatStrikethrough"; + + public override bool Evaluate(Enum a, Enum b) + { + return a != null && (b == null || !a.HasFlag(b)); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs index f5b32c35c..1b683710b 100644 --- a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs @@ -9,7 +9,7 @@ namespace Artemis.Core public override bool Evaluate(string a, string b) { - return a != null && b != null && !a.Contains(b, StringComparison.InvariantCultureIgnoreCase); + return a != null && (b == null || !a.Contains(b, StringComparison.InvariantCultureIgnoreCase)); } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index c63bbedef..f8d8a6d47 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -124,8 +124,8 @@ namespace Artemis.Core public void UpdateTimeline(double deltaTime) { // The play mode dictates whether to stick to the main segment unless the display conditions contains events - bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && DisplayConditionMet; - if (DisplayCondition != null && DisplayCondition.ContainsEvents) + bool stickToMainSegment = (Timeline.PlayMode == TimelinePlayMode.Repeat || Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle) && DisplayConditionMet; + if (DisplayCondition != null && DisplayCondition.ContainsEvents && Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle) stickToMainSegment = false; Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment); @@ -356,6 +356,7 @@ namespace Artemis.Core private DataModelConditionGroup? _displayCondition; private bool _displayConditionMet; + private bool _toggledOnByEvent = false; /// /// Gets or sets the root display condition group @@ -383,6 +384,9 @@ namespace Artemis.Core return; } + if (Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle) + _toggledOnByEvent = false; + bool conditionMet = DisplayCondition.Evaluate(); if (Parent is RenderProfileElement parent && !parent.DisplayConditionMet) conditionMet = false; @@ -398,25 +402,36 @@ namespace Artemis.Core } else if (conditionMet) { - // Event conditions reset if the timeline finished - if (Timeline.IsFinished) + if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle) { - Timeline.JumpToStart(); + _toggledOnByEvent = !_toggledOnByEvent; + if (_toggledOnByEvent) + Timeline.JumpToStart(); } - // and otherwise apply their overlap mode else { - if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart) + // Event conditions reset if the timeline finished + if (Timeline.IsFinished) + { Timeline.JumpToStart(); - else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy) - Timeline.AddExtraTimeline(); - // The third option is ignore which is handled below: + } + // and otherwise apply their overlap mode + else + { + if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart) + Timeline.JumpToStart(); + else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy) + Timeline.AddExtraTimeline(); + // The third option is ignore which is handled below: - // done + // done + } } } - DisplayConditionMet = conditionMet; + DisplayConditionMet = Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle + ? _toggledOnByEvent + : conditionMet; } #endregion diff --git a/src/Artemis.Core/Models/Profile/Timeline.cs b/src/Artemis.Core/Models/Profile/Timeline.cs index 9fb9a201f..a01b9d232 100644 --- a/src/Artemis.Core/Models/Profile/Timeline.cs +++ b/src/Artemis.Core/Models/Profile/Timeline.cs @@ -155,7 +155,7 @@ namespace Artemis.Core /// /// Gets a boolean indicating whether the timeline has finished its run /// - public bool IsFinished => (Position > Length || Length == TimeSpan.Zero) && !ExtraTimelines.Any(); + public bool IsFinished => Position > Length && !ExtraTimelines.Any(); /// /// Gets a boolean indicating whether the timeline progress has been overridden @@ -516,6 +516,11 @@ namespace Artemis.Core /// /// Play another copy of the timeline on top of the current run /// - Copy + Copy, + + /// + /// Repeat the timeline until the event fires again + /// + Toggle } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs b/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs index ead028ffb..3c0cbe854 100644 --- a/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs +++ b/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs @@ -64,6 +64,10 @@ namespace Artemis.Core.Services RegisterConditionOperator(Constants.CorePlugin, new StringNullConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringNotNullConditionOperator()); + // Enum operators + RegisterConditionOperator(Constants.CorePlugin, new EnumContainsConditionOperator()); + RegisterConditionOperator(Constants.CorePlugin, new EnumNotContainsConditionOperator()); + // Null checks, at the bottom // TODO: Implement a priority mechanism RegisterConditionOperator(Constants.CorePlugin, new NullConditionOperator()); diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml index eed55746a..be801816d 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml @@ -192,11 +192,11 @@ - + - Configure how the layer should act when the event(s) trigger before the timeline finishes + Configure how the layer should act when the event(s) trigger @@ -207,6 +207,7 @@ + + + + TOGGLE + + + + + Repeat the timeline until the event fires again + + + + + @@ -238,7 +254,7 @@ -