mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Core - Refactored display conditions
Profile editor - Added UIs for each condition type
This commit is contained in:
1 parent
66ea718316
commit
3dfc25b092
47 files changed
+1305
-546
No files matched your search
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public class AlwaysOnCondition : ICondition
|
||||
{
|
||||
public AlwaysOnCondition(RenderProfileElement profileElement)
|
||||
{
|
||||
ProfileElement = profileElement;
|
||||
Entity = new AlwaysOnConditionEntity();
|
||||
}
|
||||
|
||||
public AlwaysOnCondition(AlwaysOnConditionEntity alwaysOnConditionEntity, RenderProfileElement profileElement)
|
||||
{
|
||||
ProfileElement = profileElement;
|
||||
Entity = alwaysOnConditionEntity;
|
||||
}
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of ICondition
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConditionEntity Entity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public RenderProfileElement ProfileElement { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMet { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update()
|
||||
{
|
||||
if (ProfileElement.Parent is RenderProfileElement parent)
|
||||
IsMet = parent.DisplayConditionMet;
|
||||
else
|
||||
IsMet = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OverrideTimeline(TimeSpan position)
|
||||
{
|
||||
ProfileElement.Timeline.Override(position, position > ProfileElement.Timeline.Length);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,211 +4,262 @@ using Artemis.Core.Internal;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a condition that is based on a <see cref="DataModelEvent" />
|
||||
/// </summary>
|
||||
public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
{
|
||||
private readonly string _displayName;
|
||||
private readonly EventConditionEntity _entity;
|
||||
private readonly EventDefaultNode _eventNode;
|
||||
private DataModelPath? _eventPath;
|
||||
private DateTime _lastProcessedTrigger;
|
||||
private EventOverlapMode _overlapMode;
|
||||
private NodeScript<bool> _script;
|
||||
private EventTriggerMode _triggerMode;
|
||||
private bool _wasMet;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EventCondition" /> class
|
||||
/// </summary>
|
||||
public EventCondition(RenderProfileElement profileElement)
|
||||
{
|
||||
ProfileElement = profileElement;
|
||||
|
||||
_entity = new EventConditionEntity();
|
||||
_displayName = profileElement.GetType().Name;
|
||||
_eventNode = new EventDefaultNode {X = -300};
|
||||
_script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||
}
|
||||
|
||||
internal EventCondition(EventConditionEntity entity, RenderProfileElement profileElement)
|
||||
{
|
||||
ProfileElement = profileElement;
|
||||
|
||||
_entity = entity;
|
||||
_displayName = profileElement.GetType().Name;
|
||||
_eventNode = new EventDefaultNode();
|
||||
_script = null!;
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the script that drives the event condition
|
||||
/// </summary>
|
||||
public NodeScript<bool> Script
|
||||
{
|
||||
get => _script;
|
||||
set => SetAndNotify(ref _script, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the event that drives this event condition
|
||||
/// </summary>
|
||||
public DataModelPath? EventPath
|
||||
{
|
||||
get => _eventPath;
|
||||
set => SetAndNotify(ref _eventPath, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the condition behaves when the event fires.
|
||||
/// </summary>
|
||||
public EventTriggerMode TriggerMode
|
||||
{
|
||||
get => _triggerMode;
|
||||
set => SetAndNotify(ref _triggerMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the condition behaves when events trigger before the timeline finishes in the
|
||||
/// <see cref="EventTriggerMode.Play" /> event trigger mode.
|
||||
/// </summary>
|
||||
public EventOverlapMode OverlapMode
|
||||
{
|
||||
get => _overlapMode;
|
||||
set => SetAndNotify(ref _overlapMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the event node, applying the selected event
|
||||
/// </summary>
|
||||
public void UpdateEventNode()
|
||||
{
|
||||
IDataModelEvent? dataModelEvent = EventPath?.GetValue() as IDataModelEvent;
|
||||
_eventNode.CreatePins(dataModelEvent);
|
||||
|
||||
if (dataModelEvent != null && !Script.Nodes.Contains(_eventNode))
|
||||
Script.AddNode(_eventNode);
|
||||
else if (dataModelEvent == null && Script.Nodes.Contains(_eventNode))
|
||||
Script.RemoveNode(_eventNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start node of the event script, if any
|
||||
/// </summary>
|
||||
/// <returns>The start node of the event script, if any.</returns>
|
||||
public INode GetStartNode()
|
||||
{
|
||||
return _eventNode;
|
||||
}
|
||||
|
||||
private bool Evaluate()
|
||||
{
|
||||
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
|
||||
return false;
|
||||
|
||||
_lastProcessedTrigger = dataModelEvent.LastTrigger;
|
||||
|
||||
if (!Script.ExitNodeConnected)
|
||||
return true;
|
||||
|
||||
Script.Run();
|
||||
return Script.Result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConditionEntity Entity => _entity;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RenderProfileElement ProfileElement { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMet { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update()
|
||||
{
|
||||
_wasMet = IsMet;
|
||||
if (TriggerMode == EventTriggerMode.Toggle)
|
||||
{
|
||||
if (Evaluate())
|
||||
IsMet = !IsMet;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsMet = Evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
if (TriggerMode == EventTriggerMode.Toggle)
|
||||
{
|
||||
if (!IsMet && _wasMet)
|
||||
ProfileElement.Timeline.JumpToEnd();
|
||||
else if (IsMet && !_wasMet)
|
||||
ProfileElement.Timeline.JumpToStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsMet && ProfileElement.Timeline.IsFinished)
|
||||
{
|
||||
ProfileElement.Timeline.JumpToStart();
|
||||
}
|
||||
else if (IsMet)
|
||||
{
|
||||
if (OverlapMode == EventOverlapMode.Restart)
|
||||
ProfileElement.Timeline.JumpToStart();
|
||||
else if (OverlapMode == EventOverlapMode.Copy && ProfileElement is Layer layer)
|
||||
layer.CreateCopyAsChild();
|
||||
}
|
||||
}
|
||||
|
||||
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), TriggerMode == EventTriggerMode.Toggle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OverrideTimeline(TimeSpan position)
|
||||
{
|
||||
ProfileElement.Timeline.Override(position, TriggerMode == EventTriggerMode.Toggle && position > ProfileElement.Timeline.Length);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Script?.Dispose();
|
||||
EventPath?.Dispose();
|
||||
}
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
TriggerMode = (EventTriggerMode) _entity.TriggerMode;
|
||||
OverlapMode = (EventOverlapMode) _entity.OverlapMode;
|
||||
|
||||
if (_entity.EventPath != null)
|
||||
EventPath = new DataModelPath(_entity.EventPath);
|
||||
|
||||
Script = _entity.Script != null
|
||||
? new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile)
|
||||
: new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
_entity.TriggerMode = (int) TriggerMode;
|
||||
_entity.OverlapMode = (int) OverlapMode;
|
||||
Script.Save();
|
||||
_entity.Script = Script?.Entity;
|
||||
EventPath?.Save();
|
||||
_entity.EventPath = EventPath?.Entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public INodeScript NodeScript => Script;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void LoadNodeScript()
|
||||
{
|
||||
Script.Load();
|
||||
UpdateEventNode();
|
||||
Script.LoadConnections();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions events are fired.
|
||||
/// </summary>
|
||||
public enum EventTriggerMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a condition that is based on a <see cref="DataModelEvent" />
|
||||
/// Play the timeline once.
|
||||
/// </summary>
|
||||
public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
{
|
||||
private readonly string _displayName;
|
||||
private readonly EventConditionEntity _entity;
|
||||
private EventDefaultNode? _eventNode;
|
||||
private TimeLineEventOverlapMode _eventOverlapMode;
|
||||
private DataModelPath? _eventPath;
|
||||
private DateTime _lastProcessedTrigger;
|
||||
private NodeScript<bool>? _script;
|
||||
Play,
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EventCondition" /> class
|
||||
/// </summary>
|
||||
public EventCondition(ProfileElement profileElement)
|
||||
{
|
||||
_entity = new EventConditionEntity();
|
||||
_displayName = profileElement.GetType().Name;
|
||||
/// <summary>
|
||||
/// Toggle repeating the timeline.
|
||||
/// </summary>
|
||||
Toggle
|
||||
}
|
||||
|
||||
ProfileElement = profileElement;
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before
|
||||
/// the timeline finishes.
|
||||
/// </summary>
|
||||
public enum EventOverlapMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop the current run and restart the timeline
|
||||
/// </summary>
|
||||
Restart,
|
||||
|
||||
internal EventCondition(EventConditionEntity entity, ProfileElement profileElement)
|
||||
{
|
||||
_entity = entity;
|
||||
_displayName = profileElement.GetType().Name;
|
||||
/// <summary>
|
||||
/// Ignore subsequent event fires until the timeline finishes
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
ProfileElement = profileElement;
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the script that drives the event condition
|
||||
/// </summary>
|
||||
public NodeScript<bool>? Script
|
||||
{
|
||||
get => _script;
|
||||
set => SetAndNotify(ref _script, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the event that drives this event condition
|
||||
/// </summary>
|
||||
public DataModelPath? EventPath
|
||||
{
|
||||
get => _eventPath;
|
||||
set => SetAndNotify(ref _eventPath, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the condition behaves when events trigger before the timeline finishes
|
||||
/// </summary>
|
||||
public TimeLineEventOverlapMode EventOverlapMode
|
||||
{
|
||||
get => _eventOverlapMode;
|
||||
set => SetAndNotify(ref _eventOverlapMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the event node, applying the selected event
|
||||
/// </summary>
|
||||
public void UpdateEventNode()
|
||||
{
|
||||
if (Script == null || EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
|
||||
return;
|
||||
|
||||
if (Script.Nodes.FirstOrDefault(n => n is EventDefaultNode) is EventDefaultNode existing)
|
||||
{
|
||||
existing.UpdateDataModelEvent(dataModelEvent);
|
||||
_eventNode = existing;
|
||||
}
|
||||
else
|
||||
{
|
||||
_eventNode = new EventDefaultNode {X = -300};
|
||||
_eventNode.UpdateDataModelEvent(dataModelEvent);
|
||||
}
|
||||
|
||||
if (_eventNode.Pins.Any() && !Script.Nodes.Contains(_eventNode))
|
||||
Script.AddNode(_eventNode);
|
||||
else
|
||||
Script.RemoveNode(_eventNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Script" /> with a new empty node script
|
||||
/// </summary>
|
||||
public void CreateEmptyNodeScript()
|
||||
{
|
||||
Script?.Dispose();
|
||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||
UpdateEventNode();
|
||||
}
|
||||
|
||||
private bool Evaluate()
|
||||
{
|
||||
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
|
||||
return false;
|
||||
|
||||
_lastProcessedTrigger = dataModelEvent.LastTrigger;
|
||||
|
||||
if (Script == null)
|
||||
return true;
|
||||
|
||||
Script.Run();
|
||||
return Script.Result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConditionEntity Entity => _entity;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileElement ProfileElement { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMet { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update()
|
||||
{
|
||||
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
||||
{
|
||||
if (Evaluate())
|
||||
IsMet = !IsMet;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsMet = Evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
|
||||
{
|
||||
if (!isMet)
|
||||
{
|
||||
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
||||
timeline.JumpToEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// Event overlap mode doesn't apply in this case
|
||||
if (timeline.IsFinished)
|
||||
{
|
||||
timeline.JumpToStart();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the timeline was already running, look at the event overlap mode
|
||||
if (EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||
timeline.JumpToStart();
|
||||
else if (EventOverlapMode == TimeLineEventOverlapMode.Copy && ProfileElement is Layer layer)
|
||||
layer.CreateCopyAsChild();
|
||||
else if (EventOverlapMode == TimeLineEventOverlapMode.Toggle && !wasMet)
|
||||
timeline.JumpToStart();
|
||||
|
||||
// The remaining overlap mode is 'ignore' which requires no further action
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Script?.Dispose();
|
||||
EventPath?.Dispose();
|
||||
}
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode;
|
||||
if (_entity.Script != null)
|
||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile);
|
||||
if (_entity.EventPath != null)
|
||||
EventPath = new DataModelPath(_entity.EventPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
_entity.EventOverlapMode = (int) EventOverlapMode;
|
||||
Script?.Save();
|
||||
_entity.Script = Script?.Entity;
|
||||
EventPath?.Save();
|
||||
_entity.EventPath = EventPath?.Entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public INodeScript NodeScript => Script;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void LoadNodeScript()
|
||||
{
|
||||
if (Script == null)
|
||||
return;
|
||||
|
||||
Script.Load();
|
||||
UpdateEventNode();
|
||||
Script.LoadConnections();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
/// <summary>
|
||||
/// Play another copy of the timeline on top of the current run
|
||||
/// </summary>
|
||||
Copy
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public interface ICondition : IDisposable, IStorageModel
|
||||
/// <summary>
|
||||
/// Gets the profile element this condition applies to
|
||||
/// </summary>
|
||||
public ProfileElement ProfileElement { get; }
|
||||
public RenderProfileElement ProfileElement { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the condition is currently met
|
||||
@@ -30,12 +30,11 @@ public interface ICondition : IDisposable, IStorageModel
|
||||
void Update();
|
||||
|
||||
/// <summary>
|
||||
/// Applies the display condition to the provided timeline
|
||||
/// Updates the timeline according to the provided <paramref name="deltaTime" /> as the display condition sees fit.
|
||||
/// </summary>
|
||||
/// <param name="isMet"></param>
|
||||
/// <param name="wasMet"></param>
|
||||
/// <param name="timeline">The timeline to apply the display condition to</param>
|
||||
void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline);
|
||||
void UpdateTimeline(double deltaTime);
|
||||
|
||||
void OverrideTimeline(TimeSpan position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public class PlayOnceCondition : ICondition
|
||||
{
|
||||
public PlayOnceCondition(RenderProfileElement profileElement)
|
||||
{
|
||||
ProfileElement = profileElement;
|
||||
Entity = new PlayOnceConditionEntity();
|
||||
}
|
||||
|
||||
public PlayOnceCondition(PlayOnceConditionEntity entity, RenderProfileElement profileElement)
|
||||
{
|
||||
ProfileElement = profileElement;
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of ICondition
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConditionEntity Entity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public RenderProfileElement ProfileElement { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMet { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update()
|
||||
{
|
||||
if (ProfileElement.Parent is RenderProfileElement parent)
|
||||
IsMet = parent.DisplayConditionMet;
|
||||
else
|
||||
IsMet = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OverrideTimeline(TimeSpan position)
|
||||
{
|
||||
ProfileElement.Timeline.Override(position, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
@@ -10,11 +11,14 @@ namespace Artemis.Core
|
||||
{
|
||||
private readonly string _displayName;
|
||||
private readonly StaticConditionEntity _entity;
|
||||
private StaticPlayMode _playMode;
|
||||
private StaticStopMode _stopMode;
|
||||
private bool _wasMet;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="StaticCondition" /> class
|
||||
/// </summary>
|
||||
public StaticCondition(ProfileElement profileElement)
|
||||
public StaticCondition(RenderProfileElement profileElement)
|
||||
{
|
||||
_entity = new StaticConditionEntity();
|
||||
_displayName = profileElement.GetType().Name;
|
||||
@@ -23,7 +27,7 @@ namespace Artemis.Core
|
||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", profileElement.Profile);
|
||||
}
|
||||
|
||||
internal StaticCondition(StaticConditionEntity entity, ProfileElement profileElement)
|
||||
internal StaticCondition(StaticConditionEntity entity, RenderProfileElement profileElement)
|
||||
{
|
||||
_entity = entity;
|
||||
_displayName = profileElement.GetType().Name;
|
||||
@@ -43,31 +47,66 @@ namespace Artemis.Core
|
||||
public IConditionEntity Entity => _entity;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileElement ProfileElement { get; }
|
||||
public RenderProfileElement ProfileElement { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMet { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update()
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
||||
/// </summary>
|
||||
public StaticPlayMode PlayMode
|
||||
{
|
||||
if (!Script.ExitNodeConnected)
|
||||
{
|
||||
IsMet = true;
|
||||
return;
|
||||
}
|
||||
get => _playMode;
|
||||
set => SetAndNotify(ref _playMode, value);
|
||||
}
|
||||
|
||||
Script.Run();
|
||||
IsMet = Script.Result;
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public StaticStopMode StopMode
|
||||
{
|
||||
get => _stopMode;
|
||||
set => SetAndNotify(ref _stopMode, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
|
||||
public void Update()
|
||||
{
|
||||
if (isMet && !wasMet && timeline.IsFinished)
|
||||
timeline.JumpToStart();
|
||||
else if (!isMet && wasMet && timeline.StopMode == TimelineStopMode.SkipToEnd)
|
||||
timeline.JumpToEndSegment();
|
||||
_wasMet = IsMet;
|
||||
|
||||
// No need to run the script if the parent isn't met anyway
|
||||
bool parentConditionMet = ProfileElement.Parent is not RenderProfileElement renderProfileElement || renderProfileElement.DisplayConditionMet;
|
||||
if (!parentConditionMet)
|
||||
{
|
||||
IsMet = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Script.ExitNodeConnected)
|
||||
IsMet = true;
|
||||
else
|
||||
{
|
||||
Script.Run();
|
||||
IsMet = Script.Result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
if (IsMet && !_wasMet && ProfileElement.Timeline.IsFinished)
|
||||
ProfileElement.Timeline.JumpToStart();
|
||||
else if (!IsMet && _wasMet && StopMode == StaticStopMode.SkipToEnd)
|
||||
ProfileElement.Timeline.JumpToEndSegment();
|
||||
|
||||
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), PlayMode == StaticPlayMode.Repeat && IsMet);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OverrideTimeline(TimeSpan position)
|
||||
{
|
||||
ProfileElement.Timeline.Override(position, PlayMode == StaticPlayMode.Repeat && position > ProfileElement.Timeline.Length);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -81,12 +120,18 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
PlayMode = (StaticPlayMode) _entity.PlayMode;
|
||||
StopMode = (StaticStopMode) _entity.StopMode;
|
||||
|
||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", _entity.Script, ProfileElement.Profile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
_entity.PlayMode = (int) PlayMode;
|
||||
_entity.StopMode = (int) StopMode;
|
||||
|
||||
Script.Save();
|
||||
_entity.Script = Script.Entity;
|
||||
}
|
||||
@@ -102,4 +147,36 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions are met
|
||||
/// </summary>
|
||||
public enum StaticPlayMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Continue repeating the main segment of the timeline while the condition is met
|
||||
/// </summary>
|
||||
Repeat,
|
||||
|
||||
/// <summary>
|
||||
/// Only play the timeline once when the condition is met
|
||||
/// </summary>
|
||||
Once
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public enum StaticStopMode
|
||||
{
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, finish the the current run of the main timeline
|
||||
/// </summary>
|
||||
Finish,
|
||||
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, skip to the end segment of the timeline
|
||||
/// </summary>
|
||||
SkipToEnd
|
||||
}
|
||||
}
|
||||
@@ -237,11 +237,11 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment)
|
||||
public override void OverrideTimelineAndApply(TimeSpan position)
|
||||
{
|
||||
Timeline.Override(position, stickToMainSegment);
|
||||
DisplayCondition.OverrideTimeline(position);
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
|
||||
baseLayerEffect.InternalUpdate(Timeline);
|
||||
baseLayerEffect.InternalUpdate(Timeline); ;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -476,9 +476,9 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment)
|
||||
public override void OverrideTimelineAndApply(TimeSpan position)
|
||||
{
|
||||
Timeline.Override(position, stickToMainSegment);
|
||||
DisplayCondition.OverrideTimeline(position);
|
||||
|
||||
General.Update(Timeline);
|
||||
Transform.Update(Timeline);
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Artemis.Core
|
||||
LayerEffectsList = new List<BaseLayerEffect>();
|
||||
LayerEffects = new ReadOnlyCollection<BaseLayerEffect>(LayerEffectsList);
|
||||
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
_displayCondition = new AlwaysOnCondition(this);
|
||||
|
||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||
@@ -52,7 +53,7 @@ namespace Artemis.Core
|
||||
/// Occurs when a layer effect has been added or removed to this render element
|
||||
/// </summary>
|
||||
public event EventHandler? LayerEffectsUpdated;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
@@ -76,8 +77,10 @@ namespace Artemis.Core
|
||||
|
||||
DisplayCondition = RenderElementEntity.DisplayCondition switch
|
||||
{
|
||||
StaticConditionEntity staticConditionEntity => new StaticCondition(staticConditionEntity, this),
|
||||
EventConditionEntity eventConditionEntity => new EventCondition(eventConditionEntity, this),
|
||||
AlwaysOnConditionEntity entity => new AlwaysOnCondition(entity, this),
|
||||
PlayOnceConditionEntity entity => new PlayOnceCondition(entity, this),
|
||||
StaticConditionEntity entity => new StaticCondition(entity, this),
|
||||
EventConditionEntity entity => new EventCondition(entity, this),
|
||||
_ => DisplayCondition
|
||||
};
|
||||
|
||||
@@ -124,17 +127,11 @@ namespace Artemis.Core
|
||||
public Timeline Timeline { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Timeline" /> according to the provided <paramref name="deltaTime" /> and current display
|
||||
/// condition status
|
||||
/// Updates the <see cref="Timeline" /> according to the provided <paramref name="deltaTime" /> and current display condition
|
||||
/// </summary>
|
||||
protected void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
// TODO: Move to conditions
|
||||
|
||||
// The play mode dictates whether to stick to the main segment unless the display conditions contains events
|
||||
bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && DisplayConditionMet;
|
||||
|
||||
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
|
||||
DisplayCondition.UpdateTimeline(deltaTime);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -317,7 +314,7 @@ namespace Artemis.Core
|
||||
|
||||
OrderEffects();
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal void ActivateLayerEffect(BaseLayerEffect layerEffect)
|
||||
{
|
||||
@@ -365,13 +362,13 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets or sets the display condition used to determine whether this element is active or not
|
||||
/// </summary>
|
||||
public ICondition? DisplayCondition
|
||||
public ICondition DisplayCondition
|
||||
{
|
||||
get => _displayCondition;
|
||||
set => SetAndNotify(ref _displayCondition, value);
|
||||
}
|
||||
|
||||
private ICondition? _displayCondition;
|
||||
private ICondition _displayCondition;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the display conditions on this element and applies any required changes to the <see cref="Timeline" />
|
||||
@@ -384,14 +381,7 @@ namespace Artemis.Core
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisplayCondition == null)
|
||||
{
|
||||
DisplayConditionMet = true;
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayCondition.Update();
|
||||
DisplayCondition.ApplyToTimeline(DisplayCondition.IsMet, DisplayConditionMet, Timeline);
|
||||
DisplayConditionMet = DisplayCondition.IsMet;
|
||||
}
|
||||
|
||||
@@ -401,7 +391,6 @@ namespace Artemis.Core
|
||||
/// Overrides the main timeline to the specified time and clears any extra time lines
|
||||
/// </summary>
|
||||
/// <param name="position">The position to set the timeline to</param>
|
||||
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
|
||||
public abstract void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment);
|
||||
public abstract void OverrideTimelineAndApply(TimeSpan position);
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
||||
|
||||
private TimeSpan _position;
|
||||
private TimeSpan _lastDelta;
|
||||
private TimelinePlayMode _playMode;
|
||||
private TimelineStopMode _stopMode;
|
||||
|
||||
private TimeSpan _startSegmentLength;
|
||||
private TimeSpan _mainSegmentLength;
|
||||
private TimeSpan _endSegmentLength;
|
||||
@@ -67,23 +66,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
||||
private set => SetAndNotify(ref _lastDelta, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
||||
/// </summary>
|
||||
public TimelinePlayMode PlayMode
|
||||
{
|
||||
get => _playMode;
|
||||
set => SetAndNotify(ref _playMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public TimelineStopMode StopMode
|
||||
{
|
||||
get => _stopMode;
|
||||
set => SetAndNotify(ref _stopMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the timeline has finished its run
|
||||
@@ -388,8 +371,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
||||
StartSegmentLength = Entity.StartSegmentLength;
|
||||
MainSegmentLength = Entity.MainSegmentLength;
|
||||
EndSegmentLength = Entity.EndSegmentLength;
|
||||
PlayMode = (TimelinePlayMode) Entity.PlayMode;
|
||||
StopMode = (TimelineStopMode) Entity.StopMode;
|
||||
|
||||
JumpToEnd();
|
||||
}
|
||||
@@ -400,8 +381,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
||||
Entity.StartSegmentLength = StartSegmentLength;
|
||||
Entity.MainSegmentLength = MainSegmentLength;
|
||||
Entity.EndSegmentLength = EndSegmentLength;
|
||||
Entity.PlayMode = (int) PlayMode;
|
||||
Entity.StopMode = (int) StopMode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -413,61 +392,3 @@ internal enum TimelineSegment
|
||||
Main,
|
||||
End
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions are met
|
||||
/// </summary>
|
||||
public enum TimelinePlayMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Continue repeating the main segment of the timeline while the condition is met
|
||||
/// </summary>
|
||||
Repeat,
|
||||
|
||||
/// <summary>
|
||||
/// Only play the timeline once when the condition is met
|
||||
/// </summary>
|
||||
Once
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public enum TimelineStopMode
|
||||
{
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, finish the the current run of the main timeline
|
||||
/// </summary>
|
||||
Finish,
|
||||
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, skip to the end segment of the timeline
|
||||
/// </summary>
|
||||
SkipToEnd
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions events are fired
|
||||
/// </summary>
|
||||
public enum TimeLineEventOverlapMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop the current run and restart the timeline
|
||||
/// </summary>
|
||||
Restart,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore subsequent event fires until the timeline finishes
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// Play another copy of the timeline on top of the current run
|
||||
/// </summary>
|
||||
Copy,
|
||||
|
||||
/// <summary>
|
||||
/// Repeat the timeline until the event fires again
|
||||
/// </summary>
|
||||
Toggle
|
||||
}
|
||||
Reference in new issue
Block a user