// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik //
using System;
namespace Animancer.FSM
{
/// A static access point for the details of a key change in a .
///
/// This system is thread-safe.
///
/// Documentation: Changing States
///
/// https://kybernetik.com.au/animancer/api/Animancer.FSM/KeyChange_1
///
public struct KeyChange : IDisposable
{
/************************************************************************************************************************/
[ThreadStatic]
private static KeyChange _Current;
private IKeyedStateMachine _StateMachine;
private TKey _PreviousKey;
private TKey _NextKey;
/************************************************************************************************************************/
/// Is a of this type currently occurring?
public static bool IsActive => _Current._StateMachine != null;
/// The in which the current change is occurring.
/// This will be null if no change is currently occurring.
public static IKeyedStateMachine StateMachine => _Current._StateMachine;
/************************************************************************************************************************/
/// The key being changed from.
/// [Assert-Only]
/// is false so this property is likely being accessed on the wrong generic type.
///
public static TKey PreviousKey
{
get
{
#if UNITY_ASSERTIONS
if (!IsActive)
throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TKey), typeof(StateMachine<,>), "Key"));
#endif
return _Current._PreviousKey;
}
}
/************************************************************************************************************************/
/// The key being changed into.
/// [Assert-Only]
/// is false so this property is likely being accessed on the wrong generic type.
///
public static TKey NextKey
{
get
{
#if UNITY_ASSERTIONS
if (!IsActive)
throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TKey), typeof(StateMachine<,>), "Key"));
#endif
return _Current._NextKey;
}
}
/************************************************************************************************************************/
/// [Internal]
/// Assigns the parameters as the details of the currently active change and creates a new
/// containing the details of the previously active change so that disposing
/// it will re-assign those previous details to be current again in case of recursive state changes.
///
///
/// using (new KeyChange<TState>(previousKey, nextKey))
/// {
/// // Do the actual key change.
/// }
///
internal KeyChange(IKeyedStateMachine stateMachine, TKey previousKey, TKey nextKey)
{
this = _Current;
_Current._StateMachine = stateMachine;
_Current._PreviousKey = previousKey;
_Current._NextKey = nextKey;
}
/************************************************************************************************************************/
/// []
/// Re-assigns the values of this change (which were the previous values from when it was created) to be the
/// currently active change. See the constructor for recommended usage.
///
///
/// Usually this will be returning to default values (nulls), but if one state change causes another then the
/// second one ending will return to the first which will then return to the defaults.
///
public void Dispose()
{
_Current = this;
}
/************************************************************************************************************************/
/// Returns a string describing the contents of this .
public override string ToString() => IsActive ?
$"{nameof(KeyChange)}<{typeof(TKey).FullName}" +
$">({nameof(PreviousKey)}={PreviousKey}" +
$", {nameof(NextKey)}={NextKey})" :
$"{nameof(KeyChange)}<{typeof(TKey).FullName}(Not Currently Active)";
/// Returns a string describing the contents of the current .
public static string CurrentToString() => _Current.ToString();
/************************************************************************************************************************/
}
}