// Animancer // Copyright 2020 Kybernetik // using UnityEngine; namespace Animancer.FSM { /// <summary> /// A state that can be used in a <see cref="StateMachine{TState}"/>. /// </summary> public interface IState<TState> where TState : class, IState<TState> { /// <summary>Determines whether this state can be entered.</summary> bool CanEnterState(TState previousState); /// <summary>Determines whether this state can be exited.</summary> bool CanExitState(TState nextState); /// <summary>Called when this state is entered.</summary> void OnEnterState(); /// <summary>Called when this state is exited.</summary> void OnExitState(); } /************************************************************************************************************************/ /// <summary> /// A type of <see cref="IState{TState}"/> that knows which <see cref="StateMachine{TState}"/> it is used in so it /// can be used with various extension methods in <see cref="StateExtensions"/>. /// </summary> public interface IOwnedState<TState> : IState<TState> where TState : class, IState<TState> { /// <summary> /// The <see cref="StateMachine{TState}"/> that this state is used in. /// </summary> StateMachine<TState> OwnerStateMachine { get; } } /************************************************************************************************************************/ /// <summary> /// Various extension methods for <see cref="IOwnedState{TState}"/>. /// </summary> [HelpURL(APIDocumentationURL + "StateExtensions")] public static class StateExtensions { /************************************************************************************************************************/ /// <summary>The URL of the website where the Animancer API documentation is hosted.</summary> /// <remarks> /// This is a duplicate of <c>Strings.APIDocumentationURL</c> so that the <see cref="FSM"/> system does not /// have any dependencies on <see cref="Animancer"/> itself. /// </remarks> public const string APIDocumentationURL = "https://kybernetik.com.au/animancer/api/Animancer.FSM/"; /************************************************************************************************************************/ /// <summary> /// Checks if the specified `state` is the <see cref="StateMachine{TState}.CurrentState"/> in its /// <see cref="IOwnedState{TState}.OwnerStateMachine"/>. /// </summary> public static bool IsCurrentState<TState>(this TState state) where TState : class, IOwnedState<TState> { return state.OwnerStateMachine.CurrentState == state; } /************************************************************************************************************************/ /// <summary> /// Checks if it is currently possible to enter the specified `state`. This requires /// <see cref="IState{TState}.CanExitState"/> on the <see cref="StateMachine{TState}.CurrentState"/> and /// <see cref="IState{TState}.CanEnterState"/> on the specified `state` to both return true. /// </summary> public static bool CanEnterState<TState>(this TState state) where TState : class, IOwnedState<TState> { return state.OwnerStateMachine.CanSetState(state); } /************************************************************************************************************************/ /// <summary> /// Attempts to enter the specified `state` and returns true if successful. /// <para></para> /// This method returns true immediately if the specified `state` is already the /// <see cref="StateMachine{TState}.CurrentState"/>. To allow directly re-entering the same state, use /// <see cref="TryReEnterState"/> instead. /// </summary> public static bool TryEnterState<TState>(this TState state) where TState : class, IOwnedState<TState> { return state.OwnerStateMachine.TrySetState(state); } /************************************************************************************************************************/ /// <summary> /// Attempts to enter the specified `state` and returns true if successful. /// <para></para> /// This method does not check if the `state` is already the <see cref="StateMachine{TState}.CurrentState"/>. /// To do so, use <see cref="TryEnterState"/> instead. /// </summary> public static bool TryReEnterState<TState>(this TState state) where TState : class, IOwnedState<TState> { return state.OwnerStateMachine.TryResetState(state); } /************************************************************************************************************************/ /// <summary> /// Calls <see cref="IState{TState}.OnExitState"/> on the <see cref="StateMachine{TState}.CurrentState"/> then /// changes to the specified `state` and calls <see cref="IState{TState}.OnEnterState"/> on it. /// <para></para> /// This method does not check <see cref="IState{TState}.CanExitState"/> or /// <see cref="IState{TState}.CanEnterState"/>. To do that, you should use <see cref="TrySetState"/> instead. /// </summary> public static void ForceEnterState<TState>(this TState state) where TState : class, IOwnedState<TState> { state.OwnerStateMachine.ForceSetState(state); } /************************************************************************************************************************/ #pragma warning disable CS1587 // XML comment is not placed on a valid language element. // Copy this #region into a class which implements IOwnedState to give it the state extension methods as regular members. ///************************************************************************************************************************/ //#region State Extensions ///************************************************************************************************************************/ ///// <summary> ///// Checks if this state is the <see cref="StateMachine{TState}.CurrentState"/> in its ///// <see cref="IOwnedState{TState}.OwnerStateMachine"/>. ///// </summary> //public bool IsCurrentState() //{ // return OwnerStateMachine.CurrentState == this; //} ///************************************************************************************************************************/ ///// <summary> ///// Checks if it is currently possible to enter this state. This requires ///// <see cref="IState{TState}.CanExitState"/> on the <see cref="StateMachine{TState}.CurrentState"/> and ///// <see cref="IState{TState}.CanEnterState"/> on this state to both return true. ///// </summary> //public bool CanEnterState() //{ // return OwnerStateMachine.CanSetState(this); //} ///************************************************************************************************************************/ ///// <summary> ///// Attempts to enter this state and returns true if successful. ///// <para></para> ///// This method returns true immediately if this state is already the ///// <see cref="StateMachine{TState}.CurrentState"/>. To allow directly re-entering the same state, use ///// <see cref="TryReEnterState"/> instead. ///// </summary> //public bool TryEnterState() //{ // return OwnerStateMachine.TrySetState(this); //} ///************************************************************************************************************************/ ///// <summary> ///// Attempts to enter this state and returns true if successful. ///// <para></para> ///// This method does not check if this state is already the <see cref="StateMachine{TState}.CurrentState"/>. ///// To do so, use <see cref="TryEnterState"/> instead. ///// </summary> //public bool TryReEnterState() //{ // return OwnerStateMachine.TryResetState(this); //} ///************************************************************************************************************************/ ///// <summary> ///// Calls <see cref="IState{TState}.OnExitState"/> on the <see cref="StateMachine{TState}.CurrentState"/> then ///// changes to the this state and calls <see cref="IState{TState}.OnEnterState"/> on it. ///// <para></para> ///// This method does not check <see cref="IState{TState}.CanExitState"/> or ///// <see cref="IState{TState}.CanEnterState"/>. To do that, you should use <see cref="TrySetState"/> instead. ///// </summary> //public void ForceEnterState() //{ // OwnerStateMachine.ForceSetState(this); //} ///************************************************************************************************************************/ //#endregion ///************************************************************************************************************************/ } }