// Animancer // Copyright 2020 Kybernetik //
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Animancer.FSM
{
///
/// A simple Finite State Machine system that registers each state with a particular key.
///
/// This class allows states to be registered with a particular key upfront and then accessed later using that key.
/// See for a system that does not bother keeping track of any states other than
/// the active one.
///
[HelpURL(StateExtensions.APIDocumentationURL + "StateMachine_2")]
public partial class StateMachine : StateMachine, IDictionary
where TState : class, IState
{
/************************************************************************************************************************/
/// The collection of states mapped to a particular key.
public IDictionary Dictionary { get; set; }
/// The current state.
public TKey CurrentKey { get; private set; }
/************************************************************************************************************************/
///
/// Constructs a new with a new .
///
public StateMachine()
{
Dictionary = new Dictionary();
}
///
/// Constructs a new which uses the specified `dictionary`.
///
public StateMachine(IDictionary dictionary)
{
Dictionary = dictionary;
}
///
/// Constructs a new with a new and
/// immediately uses the `defaultKey` to enter the `defaultState`.
///
public StateMachine(TKey defaultKey, TState defaultState)
{
Dictionary = new Dictionary();
ForceSetState(defaultKey, defaultState);
}
///
/// Constructs a new which uses the specified `dictionary` and
/// immediately uses the `defaultKey` to enter the `defaultState`.
///
public StateMachine(IDictionary dictionary, TKey defaultKey, TState defaultState)
{
Dictionary = dictionary;
ForceSetState(defaultKey, defaultState);
}
/************************************************************************************************************************/
///
/// Attempts to enter the specified `state` and returns true if successful.
///
/// This method returns true immediately if the specified `state` is already the
/// . To allow directly re-entering the same state, use
/// instead.
///
public bool TrySetState(TKey key, TState state)
{
if (CurrentState == state)
return true;
else
return TryResetState(key, state);
}
///
/// Attempts to enter the specified state associated with the specified `key` and returns it if successful.
///
/// This method returns true immediately if the specified `key` is already the . To
/// allow directly re-entering the same state, use instead.
///
public TState TrySetState(TKey key)
{
if (Equals(CurrentKey, key))
return CurrentState;
else
return TryResetState(key);
}
/************************************************************************************************************************/
///
/// Attempts to enter the specified `state` and returns true if successful.
///
/// This method does not check if the `state` is already the .
/// To do so, use instead.
///
public bool TryResetState(TKey key, TState state)
{
if (!CanSetState(state))
return false;
ForceSetState(key, state);
return true;
}
///
/// Attempts to enter the specified state associated with the specified `key` and returns it if successful.
///
/// This method does not check if the `key` is already the . To do so, use
/// instead.
///
public TState TryResetState(TKey key)
{
TState state;
if (Dictionary.TryGetValue(key, out state))
{
if (TryResetState(key, state))
return state;
}
return null;
}
/************************************************************************************************************************/
///
/// Calls on the current state then changes to the specified key and
/// state and calls on it.
///
/// Note that this method does not check or
/// . To do that, you should use
/// instead.
///
public void ForceSetState(TKey key, TState state)
{
CurrentKey = key;
ForceSetState(state);
}
///
/// Uses to change to the state mapped to the `key`. If nothing is mapped,
/// it changes to default(TState).
///
public TState ForceSetState(TKey key)
{
TState state;
Dictionary.TryGetValue(key, out state);
ForceSetState(key, state);
return state;
}
/************************************************************************************************************************/
#region Dictionary Wrappers
/************************************************************************************************************************/
/// Gets or sets a particular state in the .
public TState this[TKey key] { get { return Dictionary[key]; } set { Dictionary[key] = value; } }
/// Gets an containing the keys of the .
public ICollection Keys { get { return Dictionary.Keys; } }
/// Gets an containing the state of the .
public ICollection Values { get { return Dictionary.Values; } }
/// Gets the number of states contained in the .
public int Count { get { return Dictionary.Count; } }
/// Indicates whether the is read-only.
public bool IsReadOnly { get { return Dictionary.IsReadOnly; } }
/// Adds a state to the .
public void Add(TKey key, TState state) { Dictionary.Add(key, state); }
/// Removes a state from the .
public bool Remove(TKey key) { return Dictionary.Remove(key); }
/// Removes all state from the .
public void Clear() { Dictionary.Clear(); }
/// Determines whether the contains a state with the specified `key`.
public bool ContainsKey(TKey key) { return Dictionary.ContainsKey(key); }
/// Gets the state associated with the specified `key` in the .
public bool TryGetValue(TKey key, out TState state) { return Dictionary.TryGetValue(key, out state); }
/// Adds a state to the .
public void Add(KeyValuePair item) { Dictionary.Add(item); }
/// Removes a state from the .
public bool Remove(KeyValuePair item) { return Dictionary.Remove(item); }
/// Determines whether the contains a specific value.
public bool Contains(KeyValuePair item) { return Dictionary.Contains(item); }
/// Returns an enumerator that iterates through the .
public IEnumerator> GetEnumerator() { return Dictionary.GetEnumerator(); }
/// Returns an enumerator that iterates through the .
IEnumerator IEnumerable.GetEnumerator() { return Dictionary.GetEnumerator(); }
///
/// Copies the elements of the to an `array` starting at the specified `arrayIndex`.
///
public void CopyTo(KeyValuePair[] array, int arrayIndex) { Dictionary.CopyTo(array, arrayIndex); }
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
///
/// Returns the state associated with the specified `key`, or null if none is present.
///
public TState GetState(TKey key)
{
TState state;
TryGetValue(key, out state);
return state;
}
/************************************************************************************************************************/
/// Adds the specified `keys` and `states`. Both arrays must be the same size.
public void AddRange(TKey[] keys, TState[] states)
{
Debug.Assert(keys.Length == states.Length, "Both arrays must be the same size.");
for (int i = 0; i < keys.Length; i++)
{
Dictionary.Add(keys[i], states[i]);
}
}
/************************************************************************************************************************/
/// Sets the without actually changing the state.
public void SetFakeKey(TKey key)
{
CurrentKey = key;
}
/************************************************************************************************************************/
///
/// Returns a string describing the type of this state machine and its and
/// .
///
public override string ToString()
{
return GetType().FullName + " -> " + CurrentKey + (CurrentState != null ? (" -> " + CurrentState.ToString()) : " -> null");
}
/************************************************************************************************************************/
}
}