diff --git a/Engine.Systems/Engine.Systems.csproj b/Engine.Systems/Engine.Systems.csproj
new file mode 100644
index 0000000..2ba7eb9
--- /dev/null
+++ b/Engine.Systems/Engine.Systems.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Engine.Systems/StateMachine/IState.cs b/Engine.Systems/StateMachine/IState.cs
new file mode 100644
index 0000000..ff20be4
--- /dev/null
+++ b/Engine.Systems/StateMachine/IState.cs
@@ -0,0 +1,22 @@
+namespace Syntriax.Engine.StateMachine;
+
+public interface IState
+{
+ event StateUpdateEventHandler? OnStateUpdate;
+ event StateTransitionedFromEventHandler? OnStateTransitionedFrom;
+ event StateTransitionedToEventHandler? OnStateTransitionedTo;
+ event StateTransitionReadyEventHandler? OnStateTransitionReady;
+
+ string Name { get; }
+
+ IState? GetNextState();
+
+ void Update();
+ void TransitionTo(IState from);
+ void TransitionFrom(IState to);
+
+ delegate void StateUpdateEventHandler(IState sender);
+ delegate void StateTransitionedFromEventHandler(IState sender, IState toState);
+ delegate void StateTransitionedToEventHandler(IState sender, IState fromState);
+ delegate void StateTransitionReadyEventHandler(IState sender, IState toState);
+}
diff --git a/Engine.Systems/StateMachine/State.cs b/Engine.Systems/StateMachine/State.cs
new file mode 100644
index 0000000..5ebe352
--- /dev/null
+++ b/Engine.Systems/StateMachine/State.cs
@@ -0,0 +1,52 @@
+namespace Syntriax.Engine.StateMachine;
+
+public class State : IState
+{
+ public event IState.StateUpdateEventHandler? OnStateUpdate = null;
+ public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
+ public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
+ public event IState.StateTransitionReadyEventHandler? OnStateTransitionReady = null;
+
+ private readonly List transitions = [];
+ private readonly Dictionary possibleTransitions = [];
+
+ public string Name { get; set; } = "Default State Name";
+ public IReadOnlyList Transitions => transitions;
+ public IReadOnlyDictionary PossibleTransitions => possibleTransitions;
+
+ public void RemoveTransition(string name)
+ {
+ if (!possibleTransitions.TryGetValue(name, out StateTransition stateTransition))
+ return;
+
+ transitions.Remove(stateTransition);
+ possibleTransitions.Remove(name);
+ }
+
+ public void AddTransition(string name, StateTransition stateTransition)
+ {
+ if (transitions.Contains(stateTransition))
+ return;
+
+ transitions.Add(stateTransition);
+ possibleTransitions.Add(name, stateTransition);
+ }
+
+ public void Update()
+ {
+ if (GetNextState() is IState transitionState)
+ OnStateTransitionReady?.Invoke(this, transitionState);
+ OnStateUpdate?.Invoke(this);
+ }
+
+ public void TransitionTo(IState from) => OnStateTransitionedTo?.Invoke(this, from);
+ public void TransitionFrom(IState to) => OnStateTransitionedFrom?.Invoke(this, to);
+
+ public IState? GetNextState()
+ {
+ foreach (StateTransition stateTransition in transitions)
+ if (stateTransition.CanTransition)
+ return stateTransition.State;
+ return null;
+ }
+}
diff --git a/Engine.Systems/StateMachine/StateBehaviourBase.cs b/Engine.Systems/StateMachine/StateBehaviourBase.cs
new file mode 100644
index 0000000..d51f888
--- /dev/null
+++ b/Engine.Systems/StateMachine/StateBehaviourBase.cs
@@ -0,0 +1,37 @@
+using Syntriax.Engine.Core;
+
+namespace Syntriax.Engine.StateMachine;
+
+public abstract class StateBehaviourBase : Behaviour, IState
+{
+ public event IState.StateUpdateEventHandler? OnStateUpdate = null;
+ public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
+ public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
+
+ public abstract event IState.StateTransitionReadyEventHandler? OnStateTransitionReady;
+
+ public abstract string Name { get; }
+
+ protected virtual void OnUpdateState() { }
+ public void Update()
+ {
+ OnUpdateState();
+ OnStateUpdate?.Invoke(this);
+ }
+
+ protected virtual void OnTransitionedToState(IState from) { }
+ public void TransitionTo(IState from)
+ {
+ OnTransitionedToState(from);
+ OnStateTransitionedTo?.Invoke(this, from);
+ }
+
+ protected virtual void OnTransitionedFromState(IState to) { }
+ public void TransitionFrom(IState to)
+ {
+ OnTransitionedFromState(to);
+ OnStateTransitionedFrom?.Invoke(this, to);
+ }
+
+ public abstract IState? GetNextState();
+}
diff --git a/Engine.Systems/StateMachine/StateMachine.cs b/Engine.Systems/StateMachine/StateMachine.cs
new file mode 100644
index 0000000..791427f
--- /dev/null
+++ b/Engine.Systems/StateMachine/StateMachine.cs
@@ -0,0 +1,49 @@
+using Syntriax.Engine.Core;
+
+namespace Syntriax.Engine.StateMachine;
+
+public class StateMachine : Behaviour
+{
+ public event OnStateChangedEventHandler? OnStateChanged = null;
+
+ private IState _state = new State();
+ public IState State
+ {
+ get => _state;
+ set
+ {
+ if (_state == value)
+ return;
+
+ IState previousState = _state;
+ previousState.OnStateTransitionReady -= OnStateTransitionReady;
+
+ _state = value;
+ previousState.TransitionFrom(value);
+ value.TransitionTo(_state);
+ OnStateChanged?.Invoke(this, previousState, value);
+
+ value.OnStateTransitionReady += OnStateTransitionReady;
+ }
+ }
+
+ private void OnStateTransitionReady(IState sender, IState toState)
+ {
+ State = toState;
+ while (State.GetNextState() is IState nextState)
+ State = nextState;
+ }
+
+ protected override void OnUpdate()
+ {
+ if (State is null)
+ return;
+
+ while (State.GetNextState() is IState nextState)
+ State = nextState;
+
+ State.Update();
+ }
+
+ public delegate void OnStateChangedEventHandler(StateMachine sender, IState previousState, IState newState);
+}
diff --git a/Engine.Systems/StateMachine/StateTransition.cs b/Engine.Systems/StateMachine/StateTransition.cs
new file mode 100644
index 0000000..d0cff8e
--- /dev/null
+++ b/Engine.Systems/StateMachine/StateTransition.cs
@@ -0,0 +1,11 @@
+namespace Syntriax.Engine.StateMachine;
+
+public readonly record struct StateTransition(IState State, IReadOnlyList> Conditions)
+{
+ public static implicit operator (IState state, IReadOnlyList> conditions)(StateTransition value)
+ => (value.State, value.Conditions);
+ public static implicit operator StateTransition((IState state, IReadOnlyList> conditions) value)
+ => new(value.state, value.conditions);
+
+ public bool CanTransition => !Conditions.Any(c => !c.Invoke());
+}