From 3f4c69cae251b93f5e604e9037a65ab200713e24 Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Tue, 20 Oct 2026 09:28:57 +0200 Subject: [PATCH] Core - Only let event conditions react to what happens after they start watching - A trigger from before the condition watched its event, for example long before the profile was activated, counted as new and played the element once. Triggers now count from the moment the condition starts watching its path. - A condition on a plain value treated the first value it saw as a change and played the element on the first frame. The first value is now taken as the starting value. --- .../Conditions/EventConditionTests.cs | 23 ++++++++++------ .../Profile/Conditions/EventCondition.cs | 27 +++++++++++++++++-- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs b/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs index a9dfc3a84..becbc538e 100644 --- a/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs +++ b/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs @@ -79,16 +79,24 @@ public class EventConditionTests : RenderTestBase } [Fact] - public void Play_EventTriggeredBeforeTheConditionExisted_StillPlaysTheLayer() + public void Play_EventTriggeredBeforeTheConditionExisted_DoesNotPlayTheLayer() { DataModel.Event.Trigger(); (Layer layer, _) = AddLayer(); using RenderFrame frame = Render(layer.Profile); - // Pinned as-is: the condition starts out having seen no triggers at all, so a trigger from before it was created - // (for example long before the profile was activated) still counts as new and plays the layer once. Looks like - // the condition should only react to triggers that happen after it started watching. + ShouldBeOff(frame); + } + + [Fact] + public void Play_EventTriggeredAfterTheConditionExisted_PlaysTheLayer_EvenBeforeTheFirstFrame() + { + (Layer layer, _) = AddLayer(); + DataModel.Event.Trigger(); + + using RenderFrame frame = Render(layer.Profile); + ShouldBeOn(frame); } @@ -416,15 +424,14 @@ public class EventConditionTests : RenderTestBase } [Fact] - public void PathToAValue_PlaysOnTheFirstFrame() + public void PathToAValue_DoesNotPlayOnTheFirstFrame() { (Layer layer, _) = AddLayer(eventPath: "Integer"); using RenderFrame frame = Render(layer.Profile); - // Pinned as-is: the last seen value starts out empty, so the first frame always sees the value as changed (0 is - // not empty) and plays the layer even though nothing changed. - ShouldBeOn(frame); + // The first frame only learns the starting value, nothing changed yet + ShouldBeOff(frame); } [Fact] diff --git a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs index bbe50bdc1..acf68a02d 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs @@ -17,8 +17,10 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition private NodeScript _script; private DefaultNode _startNode; private DataModelPath? _eventPath; - private DateTime _lastProcessedTrigger; + // Only what happens after the condition starts watching its path counts, see StartWatching + private DateTime _lastProcessedTrigger = DateTime.Now; private object? _lastProcessedValue; + private bool _hasProcessedValue; private EventOverlapMode _overlapMode; private EventToggleOffMode _toggleOffMode; private EventTriggerMode _triggerMode; @@ -64,7 +66,11 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition public DataModelPath? EventPath { get => _eventPath; - set => SetAndNotify(ref _eventPath, value); + set + { + if (SetAndNotify(ref _eventPath, value)) + StartWatching(); + } } /// @@ -243,6 +249,15 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition Script.AddNode(newStartNode); } + private void StartWatching() + { + // Triggers and values from before the condition watched the path don't count, otherwise for example an event that + // triggered long before the profile was activated would still play the element once + _lastProcessedTrigger = DateTime.Now; + _lastProcessedValue = null; + _hasProcessedValue = false; + } + private bool Evaluate() { if (EventPath == null) @@ -258,6 +273,14 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition } else if (_startNode is EventConditionValueChangedStartNode valueChangedNode) { + // The first value seen is where the value starts, not a change + if (!_hasProcessedValue) + { + _lastProcessedValue = value; + _hasProcessedValue = true; + return false; + } + if (Equals(value, _lastProcessedValue)) return false;