// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
namespace FlaxEngine.Utilities
{
///
/// State machine logic pattern
///
public abstract class StateMachine
{
///
/// The current state.
///
protected State currentState;
///
/// The states.
///
protected readonly List states = new List();
///
/// Gets the current state.
///
public State CurrentState => currentState;
///
/// Occurs when state is being changed.
///
public event Action StateChanging;
///
/// Occurs when state gets changed.
///
public event Action StateChanged;
///
/// Gets the states (read-only).
///
public IReadOnlyList States => states;
///
/// Gets state of given type.
///
/// The type of the state.
public State GetState()
{
return states.Find(x => x.GetType() == typeof(TStateType));
}
///
/// Goes to the state.
///
/// The type of the state.
/// Cannot find state of given type.
public void GoToState()
{
var state = states.Find(x => x.GetType() == typeof(TStateType));
if (state == null)
throw new InvalidOperationException($"Cannot find state {typeof(TStateType)}.");
GoToState(state);
}
///
/// Goes to the state.
///
/// The target state.
/// state
public virtual void GoToState(State state)
{
if (state == null)
throw new ArgumentNullException(nameof(state));
// Prevent from entering the same state
if (state == currentState)
return;
// Check if cannot leave current state
if (currentState != null && !currentState.CanExit(state))
return;
// Check if cannot enter new state
if (!state.CanEnter())
return;
// Change state
SwitchState(state);
}
///
/// Adds the state.
///
/// The state.
public virtual void AddState(State state)
{
if (state.owner == this)
throw new InvalidOperationException("Cannot add already registered state.");
states.Add(state);
state.owner = this;
}
///
/// Removes the state.
///
/// The state.
public virtual void RemoveState(State state)
{
if (state.owner == null)
throw new InvalidOperationException("Cannot remove unregistered state.");
states.Remove(state);
state.owner = null;
}
///
/// Switches the state.
///
/// Then next state.
protected virtual void SwitchState(State nextState)
{
StateChanging?.Invoke();
currentState?.OnExit(nextState);
currentState = nextState;
currentState?.OnEnter();
StateChanged?.Invoke();
}
}
}